题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2874
题目大意:给出n个点,m条边,q个询问,每次询问(u,v)的最短距离,若(u,v)不连通即不在同一颗树上则输出“Not connected”。
解题思路:这题也是模板题,有所不同的是这次给出的是森林而不是一棵树,所以vis数组得稍作修改,标记vis数组的是当前树的编号。下面给出Tarjan和倍增法两种解法。
Tarjan(离线)写法,被MLE坑了,离线写法必须要用静态邻接表,因为虽然n不大,但是q很大,所以如果存储问题邻接表会超内存。
还有突然脑残把dis[x]+dis[y]-2*dis[lca(x,y)]写成dis[x]+dis[y]-dis[lca(x,y)]漏了个2看了半天!!!MDZZ!!!
代码
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include<vector> 5 #include<algorithm> 6 using namespace std; 7 const int N=1e4+5; 8 const int M=1e6+5; 9 10 struct qnode{ 11 int to,id,next; 12 }q[M*2]; 13 14 struct node{ 15 int to,w,next; 16 }edge[N*2]; 17 18 int n,m,cnt,num,idx1,idx2; 19 int res[M],root[N],vis[N],dis[N],head1[N],head2[N]; 20 21 void addedge(int u,int v,int w){ 22 edge[idx1].to=v; 23 edge[idx1].w=w; 24 edge[idx1].next=head1[u]; 25 head1[u]=idx1++; 26 } 27 28 void addq(int u,int v,int id){ 29 q[idx2].to=v; 30 q[idx2].id=id; 31 q[idx2].next=head2[u]; 32 head2[u]=idx2++; 33 } 34 35 int find(int x){ 36 return root[x]==x?x:root[x]=find(root[x]); 37 } 38 39 void init(){ 40 cnt=num=0; 41 idx1=idx2=1; 42 for(int i=1;i<=n;i++) root[i]=i; 43 memset(res,-1,sizeof(res)); 44 memset(vis,0,sizeof(vis)); 45 memset(dis,0,sizeof(dis)); 46 memset(head1,0,sizeof(head1)); 47 memset(head2,0,sizeof(head2)); 48 } 49 50 void lca(int u,int num){ 51 //注意这里vis[u]标记的是树的编号,为了判断点是否在同一颗树上 52 vis[u]=num; 53 //root[u]=u; 54 for(int i=head1[u];i;i=edge[i].next){ 55 node t=edge[i]; 56 if(!vis[t.to]){ 57 dis[t.to]=dis[u]+t.w; 58 lca(t.to,num); 59 root[t.to]=u; 60 } 61 } 62 for(int i=head2[u];i;i=q[i].next){ 63 qnode t=q[i]; 64 //属于同一颗树 65 if(vis[t.to]==num) 66 res[t.id]=dis[t.to]+dis[u]-2*dis[find(t.to)]; 67 } 68 } 69 70 int main(){ 71 int t; 72 while(~scanf("%d%d%d",&n,&m,&t)){ 73 init(); 74 for(int i=1;i<=m;i++){ 75 int a,b,w; 76 scanf("%d%d%d",&a,&b,&w); 77 addedge(a,b,w); 78 addedge(b,a,w); 79 } 80 for(int i=1;i<=t;i++){ 81 int a,b; 82 scanf("%d%d",&a,&b); 83 addq(a,b,i); 84 addq(b,a,i); 85 } 86 //森林 87 for(int i=1;i<=n;i++){ 88 if(!vis[i]) lca(i,i); 89 } 90 for(int i=1;i<=t;i++){ 91 if(res[i]==-1) 92 puts("Not connected"); 93 else 94 printf("%d ",res[i]); 95 } 96 } 97 return 0; 98 }
倍增法(在线)写法,这个倒是不卡vector了,解决了上面所有问题后,我又脑残了的,我TM忘记调用bz()函数了!!!干瞪着瞪了两个小时两个小时啊!!!
所以这道水题弄了我一个下午。。。。噗!
代码
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include<vector> 5 #include<algorithm> 6 using namespace std; 7 const int N=1e5+5; 8 9 struct node{ 10 int to,w; 11 node(int to,int w):to(to),w(w){} 12 }; 13 14 int n,m,q; 15 int fa[N][31],depth[N],vis[N],dis[N]; 16 vector<node>v[N]; 17 18 void init(){ 19 for(int i=1;i<=n;i++) v[i].clear(); 20 memset(vis,0,sizeof(vis)); 21 memset(depth,0,sizeof(depth)); 22 memset(dis,0,sizeof(dis)); 23 memset(fa,0,sizeof(fa)); 24 } 25 26 void dfs(int u,int num){ 27 vis[u]=num; 28 for(int i=0;i<v[u].size();i++){ 29 node t=v[u][i]; 30 if(!vis[t.to]){ 31 depth[t.to]=depth[u]+1; 32 fa[t.to][0]=u; 33 dis[t.to]=dis[u]+t.w; 34 dfs(t.to,num); 35 } 36 } 37 } 38 39 //倍增,处理fa数组 40 void bz(){ 41 for(int j=1;j<=30;j++){ 42 for(int i=1;i<=n;i++){ 43 fa[i][j]=fa[fa[i][j-1]][j-1]; 44 } 45 } 46 } 47 48 int lca(int x,int y){ 49 //保证深度大的点为x 50 if(depth[x]<depth[y]) 51 swap(x,y); 52 int dc=depth[x]-depth[y]; 53 for(int i=0;i<30;i++){ 54 if(1<<i&dc) //一个判断,模拟下就会清楚 55 x=fa[x][i]; 56 } 57 if(x==y) return x; //如果深度一样,两个点相同,直接返回 58 for(int i=29;i>=0;i--){ 59 if(fa[x][i]!=fa[y][i]){ //跳2^i不一样,就跳,否则不跳 60 x=fa[x][i]; 61 y=fa[y][i]; 62 } 63 } 64 return fa[x][0]; 65 } 66 67 int main(){ 68 while(~scanf("%d%d%d",&n,&m,&q)){ 69 init(); //初始化 70 for(int i=1;i<=m;i++){ 71 int a,b,w; 72 scanf("%d%d%d",&a,&b,&w); 73 v[a].push_back(node(b,w)); 74 v[b].push_back(node(a,w)); 75 } 76 for(int i=1;i<=n;i++){ 77 if(!vis[i]) dfs(i,i); 78 } 79 bz(); //一定别忘了啊!!! 80 for(int i=1;i<=q;i++){ 81 int x,y; 82 scanf("%d%d",&x,&y); 83 if(vis[x]!=vis[y]) 84 puts("Not connected"); 85 else 86 printf("%d ",dis[x]+dis[y]-2*dis[lca(x,y)]); 87 } 88 } 89 return 0; 90 }