HDU 2874 Connections between cities(LCA(离线、在线)求树上距离+森林)

题目链接: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 }
原文地址:https://www.cnblogs.com/fu3638/p/8644784.html