您的位置:首页 > 其它

【NOIP2013模拟】Freda的传呼机

2016-05-25 19:35 281 查看

Description

给出一张n个点,m条边的无向联通图,和q次询问,每次询问x到y的最短路。

n,q<=10^4,m<=1.2*10^4

时限100ms

10% n<=10^3,m<=1.2*10^3

另外30% n=m+1

另外50% n=m

Solution

这题画风突然就变了。

10%暴力,30%树,50%换套树,10%。。。仙人掌!@#¥%……&*

还开100ms,吓得我都不敢打玄学算法sp(b)fa了.

不过事实证明出题人不卡。。。

首先我们从root开始跑一遍sp(b)fa

然后把仙人掌变成树

链接一个把仙人掌变成树的方法

这样我们就得到了一颗仙人掌树(是这样叫的吧?)

并且这棵树中的所有环顶(父亲)都是儿子的spfa必经点。

那么对于两个点x,y,我们我们把他们往上跳,跳到lca的下面,设为a和b。

如果这两个点不属于一个一个环,那么答案就是dis[x]+dis[y]-2*dis[lca]

如果属于,那么我们就用dis[x]-dis[a]+dis[y]-dis[b]再加上a到b走环的距离(min一下就好了)

infleaking同学竟然比我早A,不爽(*  ̄︿ ̄)

Code

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define rep(i,a) for(int i=last[a];i;i=next[i])
#define N 22005
using namespace std;
int n,m,x,y,z,l,q,tot;
int d
,c
,dfn
,dis
,cir
,p
,len
;
int t[N*2],v[N*2],next[N*2],last
,fa
[17],que[N*5];
bool e[N*2],bz
;
void add(int x,int y,int z) {
t[++l]=y;v[l]=z;next[l]=last[x];last[x]=l;
}
void spfa() {
memset(dis,127,sizeof(dis));dis[1]=0;
int i=0,j=1;bz[1]=1;que[1]=1;
while (i<j) {
rep(k,que[++i]) if (dis[t[k]]>dis[que[i]]+v[k]) {
dis[t[k]]=dis[que[i]]+v[k];
if (!bz[t[k]]) bz[t[k]]=1,que[++j]=t[k];
}
bz[que[i]]=0;
}
}
void cate(int S,int T,int line) {
e[line]=e[line^1]=1;c[++c[0]]=v[line];
for(int i=T;i!=S;i=t[p[i]^1]) {
cir[i]=c[0];e[p[i]]=e[p[i]^1]=1;
c[c[0]]+=v[p[i]];add(S,i,0);add(i,S,0);
}
}
void dfs(int x) {
dfn[x]=++tot;
rep(i,x) if (i<=2*m+1&&i!=(p[x]^1))
if (!dfn[t[i]]) {
p[t[i]]=i;len[t[i]]=len[x]+v[i];
dfs(t[i]);
} else if (dfn[t[i]]<dfn[x]) cate(t[i],x,i);
}
void make(int x,int y) {
d[x]=d[y]+1;fa[x][0]=y;
rep(i,x) if (t[i]!=y&&!e[i]) make(t[i],x);
}
int lca(int &x,int &y) {
if (d[x]<d[y]) swap(x,y);
fd(j,16,0) if (d[fa[x][j]]>=d[y]) x=fa[x][j];
if (x==y) return x;
fd(j,16,0) if (fa[x][j]!=fa[y][j]) x=fa[x][j],y=fa[y][j];
return fa[x][0];
}
int main() {
scanf("%d%d%d",&n,&m,&q);l=1;
fo(i,1,m) scanf("%d%d%d",&x,&y,&z),add(x,y,z),add(y,x,z);
spfa();dfs(1);make(1,0);
fo(j,1,16) fo(i,1,n) fa[i][j]=fa[fa[i][j-1]][j-1];
for(;q;q--) {
scanf("%d%d",&x,&y);int a=x,b=y;z=lca(x,y);
if (cir[x]&&cir[y]&&cir[x]==cir[y]) {
z=abs(len[x]-len[y]);
printf("%d\n",dis[a]-dis[x]+dis[b]-dis[y]+min(z,c[cir[x]]-z));
continue;
}
printf("%d\n",dis[a]+dis[b]-2*dis[z]);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: