专题训练之LCA

推荐几个博客:https://www.cnblogs.com/JVxie/p/4854719.html Tarjan离线算法的基本思路及其算法实现

https://blog.csdn.net/shahdza/article/details/7779356 LCA题集

模板(题):

1.(POJ1470)http://poj.org/problem?id=1470

Tarjan离线算法

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 #include<iostream>
  5 using namespace std;
  6 const int maxn=1010;
  7 const int maxm=500010;
  8 struct Edge{
  9     int to,nxt;
 10 }edge[maxn*2];
 11 struct Query{
 12     int q,nxt;
 13     int index;
 14 }query[maxm*2];
 15 int f[maxn],anc[maxn];
 16 bool vis[maxn];
 17 int head[maxn],tot;
 18 int ans[maxm],h[maxm],tt,Q;
 19 bool flag[maxn];
 20 int num[maxn];
 21 
 22 int find(int x)
 23 {
 24     if ( f[x]==-1 ) return x;
 25     return f[x]=find(f[x]);
 26 }
 27 
 28 void merge(int x,int y)
 29 {
 30     int fx=find(x);
 31     int fy=find(y);
 32     if ( fx!=fy ) f[fx]=fy;
 33 }
 34 
 35 void addedge(int u,int v)
 36 {
 37     edge[tot].to=v;
 38     edge[tot].nxt=head[u];
 39     head[u]=tot++;
 40 }
 41 
 42 void addquery(int u,int v,int index)
 43 {
 44     query[tt].q=v;
 45     query[tt].nxt=h[u];
 46     query[tt].index=index;
 47     h[u]=tt++;
 48     query[tt].q=u;
 49     query[tt].nxt=h[v];
 50     query[tt].index=index;
 51     h[v]=tt++;
 52 }
 53 
 54 void init()
 55 {
 56     tot=0;
 57     memset(head,-1,sizeof(head));
 58     tt=0;
 59     memset(h,-1,sizeof(h));
 60     memset(vis,false,sizeof(vis));
 61     memset(f,-1,sizeof(f));
 62     memset(anc,0,sizeof(anc));
 63 }
 64 
 65 void LCA(int u)
 66 {
 67     anc[u]=u;
 68     vis[u]=true;
 69     for ( int i=head[u];i!=-1;i=edge[i].nxt )
 70     {
 71         int v=edge[i].to;
 72         if ( vis[v] ) continue;
 73         LCA(v);
 74         merge(u,v);
 75         anc[find(u)]=u;
 76     }
 77     for ( int i=h[u];i!=-1;i=query[i].nxt )
 78     {
 79         int v=query[i].q;
 80         if ( vis[v] ) ans[query[i].index]=anc[find(v)];
 81     }
 82 }
 83 
 84 int main()
 85 {
 86     int n,u,v,k;
 87     while ( scanf("%d",&n)!=EOF )
 88     {
 89         init();
 90         memset(flag,false,sizeof(flag));
 91         for ( int i=1;i<=n;i++ )
 92         {
 93             scanf("%d:(%d)",&u,&k);
 94             while ( k-- )
 95             {
 96                 scanf("%d",&v);
 97                 flag[v]=true;
 98                 addedge(u,v);
 99                 addedge(v,u);
100             }
101         }
102         scanf("%d",&Q);
103         for ( int i=0;i<Q;i++ )
104         {
105             char ch;
106             cin>>ch;
107             scanf("%d %d)",&u,&v);
108             addquery(u,v,i);
109         }
110         int root;
111         for ( int i=1;i<=n;i++ )
112         {
113             if ( !flag[i] )
114             {
115                 root=i;
116                 break;
117             }
118         }
119         LCA(root);
120         memset(num,0,sizeof(num));
121         for ( int i=0;i<Q;i++ ) num[ans[i]]++;
122         for ( int i=1;i<=n;i++ )
123         {
124             if ( num[i]>0 ) printf("%d:%d\n",i,num[i]);
125         }
126     }
127     return 0;
128 }
POJ1470

2.(POJ1330)http://poj.org/problem?id=1330

倍增法在线算法

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 #include<queue>
  5 using namespace std;
  6 const int maxn=1e4+10;
  7 const int DEG=20;
  8 struct Edge{
  9     int to,nxt;
 10 }edge[maxn*2];
 11 int head[maxn],tot;
 12 int fa[maxn][DEG]; //fa[i][j]表示节点i的第2^j个祖先 
 13 int deg[maxn];
 14 bool flag[maxn];
 15 
 16 void addedge(int u,int v)
 17 {
 18     edge[tot].to=v;
 19     edge[tot].nxt=head[u];
 20     head[u]=tot++;
 21 }
 22 
 23 void init()
 24 {
 25     tot=0;
 26     memset(head,-1,sizeof(head));
 27 }
 28 
 29 void BFS(int root)
 30 {
 31     queue<int>que;
 32     deg[root]=0;
 33     fa[root][0]=root;
 34     que.push(root);
 35     while ( !que.empty() ) 
 36     {
 37         int tmp=que.front();
 38         que.pop();
 39         for ( int i=1;i<DEG;i++ ) fa[tmp][i]=fa[fa[tmp][i-1]][i-1];
 40         for ( int i=head[tmp];i!=-1;i=edge[i].nxt )
 41         {
 42             int v=edge[i].to;
 43             if ( v==fa[tmp][0] ) continue;
 44             deg[v]=deg[tmp]+1;
 45             fa[v][0]=tmp;
 46             que.push(v);
 47         }
 48     }
 49 }
 50 
 51 int LCA(int u,int v)
 52 {
 53     if ( deg[u]>deg[v] ) swap(u,v);
 54     int hu=deg[u],hv=deg[v];
 55     int tu=u,tv=v;
 56     for ( int det=hv-hu,i=0;det;det>>=1,i++ )
 57     {
 58         if ( det&1 ) tv=fa[tv][i];
 59     }
 60     if ( tu==tv ) return tu;
 61     for ( int i=DEG-1;i>=0;i-- )
 62     {
 63         if ( fa[tu][i]==fa[tv][i] ) continue;
 64         tu=fa[tu][i];
 65         tv=fa[tv][i];
 66     }
 67     return fa[tu][0];
 68 }
 69 
 70 int main()
 71 {
 72     int T,n,u,v;
 73     scanf("%d",&T);
 74     while ( T-- )
 75     {
 76         scanf("%d",&n);
 77         init();
 78         memset(flag,false,sizeof(flag));
 79         for ( int i=1;i<n;i++ )
 80         {
 81             scanf("%d%d",&u,&v);
 82             addedge(u,v);
 83             addedge(v,u);
 84             flag[v]=true;
 85         }
 86         int root;
 87         for ( int i=1;i<=n;i++ )
 88         {
 89             if ( !flag[i] )
 90             {
 91                 root=i;
 92                 break;
 93             }
 94         }
 95         BFS(root);
 96         scanf("%d%d",&u,&v);
 97         printf("%d\n",LCA(u,v));
 98     }
 99     return 0;
100 }
POJ1330

练习题:

1.(HDOJ2586)http://acm.hdu.edu.cn/showproblem.php?pid=2586

题意:求给定两点之间的距离

分析:如果t是u,v的最近公共祖先,那么d[u,v]=d[u,root]+d[v,root]-2*d[t,root],所以我们只需要在倍增算法的BFS中每次更新深度时同时把距离一起更新掉即可

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 #include<queue>
  5 using namespace std;
  6 const int maxn=4e4+10;
  7 const int DEG=20;
  8 const int inf=1e9;
  9 struct Edge{
 10     int to,nxt,w;
 11 }edge[maxn*2];
 12 int head[maxn],tot;
 13 int fa[maxn][DEG]; //fa[i][j]表示节点i的第2^j个祖先 
 14 int deg[maxn];
 15 bool flag[maxn];
 16 int n,d[maxn];
 17 
 18 void addedge(int u,int v,int w)
 19 {
 20     edge[tot].to=v;
 21     edge[tot].nxt=head[u];
 22     edge[tot].w=w;
 23     head[u]=tot++;
 24 }
 25 
 26 void init()
 27 {
 28     tot=0;
 29     memset(head,-1,sizeof(head));
 30 }
 31 
 32 void BFS(int root)
 33 {
 34     queue<int>que;
 35     for ( int i=1;i<=n;i++ ) d[i]=inf;
 36     d[root]=0;
 37     deg[root]=0;
 38     fa[root][0]=root;
 39     que.push(root);
 40     while ( !que.empty() ) 
 41     {
 42         int tmp=que.front();
 43         que.pop();
 44         for ( int i=1;i<DEG;i++ ) fa[tmp][i]=fa[fa[tmp][i-1]][i-1];
 45         for ( int i=head[tmp];i!=-1;i=edge[i].nxt )
 46         {
 47             int v=edge[i].to;
 48             int w=edge[i].w;
 49             if ( v==fa[tmp][0] ) continue;
 50             deg[v]=deg[tmp]+1;
 51             d[v]=min(d[v],d[tmp]+w);
 52             fa[v][0]=tmp;
 53             que.push(v);
 54         }
 55     }
 56 }
 57 
 58 int LCA(int u,int v)
 59 {
 60     if ( deg[u]>deg[v] ) swap(u,v);
 61     int hu=deg[u],hv=deg[v];
 62     int tu=u,tv=v;
 63     for ( int det=hv-hu,i=0;det;det>>=1,i++ )
 64     {
 65         if ( det&1 ) tv=fa[tv][i];
 66     }
 67     if ( tu==tv ) return tu;
 68     for ( int i=DEG-1;i>=0;i-- )
 69     {
 70         if ( fa[tu][i]==fa[tv][i] ) continue;
 71         tu=fa[tu][i];
 72         tv=fa[tv][i];
 73     }
 74     return fa[tu][0];
 75 }
 76 
 77 int main()
 78 {
 79     int T,u,v,w,m,x,ans;
 80     scanf("%d",&T);
 81     while ( T-- )
 82     {
 83         scanf("%d%d",&n,&m);
 84         init();
 85         memset(flag,false,sizeof(flag));
 86         for ( int i=1;i<n;i++ )
 87         {
 88             scanf("%d%d%d",&u,&v,&w);
 89             addedge(u,v,w);
 90             addedge(v,u,w);
 91             flag[v]=true;
 92         }
 93         int root;
 94         for ( int i=1;i<=n;i++ )
 95         {
 96             if ( !flag[i] )
 97             {
 98                 root=i;
 99                 break;
100             }
101         }
102         BFS(root);
103         for ( int i=1;i<=m;i++ )
104         {
105             scanf("%d%d",&u,&v);
106             x=LCA(u,v);
107             ans=d[u]+d[v]-2*d[x];
108             printf("%d\n",ans);
109         }
110     }
111     return 0;
112 }
HDOJ2586

2.(HDOJ2874)http://acm.hdu.edu.cn/showproblem.php?pid=2874

题意:给出一个森林,求给定两点之间的距离

分析:Tarjan离线算法,有多个根节点,每次询问时判断两个点是否处于同一个树上。LCA传入参数时需要传入当前的点,距离根节点的距离,根节点的编号。但是此题卡内存

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 #include<iostream>
  5 using namespace std;
  6 const int maxn=1e4+10;
  7 const int maxm=1e6+10;
  8 struct Edge{
  9     int to,nxt,w;
 10 }edge[maxn*2];
 11 struct Query{
 12     int q,nxt;
 13     int index;
 14 }query[maxm*2];
 15 int f[maxn],anc[maxn];
 16 int head[maxn],tot;
 17 int ans[maxm],h[maxm],tt,Q,belong[maxn];
 18 int n,d[maxn];
 19 
 20 int find(int x)
 21 {
 22     if ( f[x]==-1 ) return x;
 23     return f[x]=find(f[x]);
 24 }
 25 
 26 void merge(int x,int y)
 27 {
 28     int fx=find(x);
 29     int fy=find(y);
 30     if ( fx!=fy ) f[fx]=fy;
 31 }
 32 
 33 void addedge(int u,int v,int w)
 34 {
 35     edge[tot].to=v;
 36     edge[tot].nxt=head[u];
 37     edge[tot].w=w;
 38     head[u]=tot++;
 39 }
 40 
 41 void addquery(int u,int v,int index)
 42 {
 43     query[tt].q=v;
 44     query[tt].nxt=h[u];
 45     query[tt].index=index;
 46     h[u]=tt++;
 47     query[tt].q=u;
 48     query[tt].nxt=h[v];
 49     query[tt].index=index;
 50     h[v]=tt++;
 51 }
 52 
 53 void init()
 54 {
 55     tot=0;
 56     memset(head,-1,sizeof(head));
 57     tt=0;
 58     memset(h,-1,sizeof(h));
 59     memset(f,-1,sizeof(f));
 60     memset(anc,0,sizeof(anc));
 61 }
 62 
 63 void LCA(int u,int deep,int root)
 64 {
 65     anc[u]=u;
 66     belong[u]=root;
 67     d[u]=deep;
 68     for ( int i=head[u];i!=-1;i=edge[i].nxt )
 69     {
 70         int v=edge[i].to;
 71         int w=edge[i].w;
 72         if ( belong[v]!=-1 ) continue;
 73         LCA(v,deep+w,root);
 74         merge(u,v);
 75         anc[find(u)]=u;
 76     }
 77     for ( int i=h[u];i!=-1;i=query[i].nxt )
 78     {
 79         int v=query[i].q;
 80         if ( belong[v]==root ) 
 81         {
 82             int sum=d[u]+d[v]-2*d[anc[find(v)]];
 83             ans[query[i].index]=sum;
 84         }
 85     }
 86 }
 87 
 88 int main()
 89 {
 90     int u,v,w,k,m,q,x;
 91     while ( scanf("%d%d%d",&n,&m,&Q)!=EOF )
 92     {
 93         init();
 94         for ( int i=1;i<=m;i++ )
 95         {
 96             scanf("%d%d%d",&u,&v,&w);
 97             addedge(u,v,w);
 98             addedge(v,u,w);
 99         }
100         for ( int i=0;i<Q;i++ )
101         {
102             ans[i]=-1;
103             scanf("%d%d",&u,&v);
104             addquery(u,v,i);
105         }
106         memset(belong,-1,sizeof(belong));
107         memset(d,-1,sizeof(d));
108         for ( int i=1;i<=n;i++ )
109         {
110             if ( belong[i]==-1 ) LCA(i,0,i);
111         }
112         for ( int i=0;i<Q;i++ ) 
113         {
114             if ( ans[i]!=-1 ) printf("%d\n",ans[i]);
115             else printf("Not connected\n");    
116         }
117     }
118     return 0;
119 }
HDOJ2874(MLE)
  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 #include<queue>
  5 #include<vector>
  6 using namespace std;
  7 typedef long long LL;
  8 
  9 const int maxm=2e4+10;
 10 const int maxn=1e4+10;
 11 const int maxq=2e6+10;
 12 struct Node{
 13     int to;
 14     int w;
 15     int next;
 16 }e[maxm];
 17 int eh[maxn],dis[maxn],pre[maxn],etol,vis[maxn];
 18 struct Query{
 19     int to;
 20     int index;
 21     int next;
 22 }qe[maxq];
 23 int qh[maxn],ans[maxq/2],qtol;
 24 int n,m,c;
 25 
 26 void init()
 27 {
 28     etol=qtol=0;
 29     memset(eh,-1,sizeof(eh));
 30     memset(qh,-1,sizeof(qh));
 31 }
 32 
 33 void add1(int u,int v,int w)
 34 {
 35     e[etol].to=v;
 36     e[etol].w=w;
 37     e[etol].next=eh[u];
 38     eh[u]=etol++;
 39 }
 40 
 41 void add2(int u,int v,int id)
 42 {
 43     qe[qtol].index=id;
 44     qe[qtol].to=v;
 45     qe[qtol].next=qh[u];
 46     qh[u]=qtol++;
 47 }
 48 
 49 int Find(int u)
 50 {
 51     if(pre[u]!=u) pre[u]=Find(pre[u]);
 52     return pre[u];
 53 }
 54 
 55 void LCA(int u,int deep,int root)
 56 {
 57     pre[u]=u;
 58     dis[u]=deep;
 59     vis[u]=root;
 60     for(int i=eh[u];~i;i=e[i].next)
 61     {
 62         int v=e[i].to;
 63         if(vis[v]==-1)
 64         {
 65             LCA(v,deep+e[i].w,root);
 66             pre[v]=u;
 67         }
 68     }
 69     for(int i=qh[u];~i;i=qe[i].next)
 70     {
 71         int v=qe[i].to;
 72         if(vis[v]==root)
 73             ans[qe[i].index]=dis[v]+dis[u]-2*dis[Find(v)];
 74     }
 75 }
 76 
 77 
 78 int main()
 79 {
 80     while(~scanf("%d%d%d",&n,&m,&c))
 81     {
 82         int u,v,w;
 83         init();
 84         while(m--)
 85         {
 86             scanf("%d%d%d",&u,&v,&w);
 87             add1(u,v,w);
 88             add1(v,u,w);    
 89         }
 90         for(int i=0;i<c;i++)
 91         {
 92             scanf("%d%d",&u,&v);
 93             ans[i]=-1;
 94             add2(u,v,i);
 95             add2(v,u,i);
 96         }
 97         memset(vis,-1,sizeof(vis));
 98         for(int i=1;i<=n;i++){
 99             if(vis[i]==-1)
100                 LCA(i,0,i);
101         }
102         for(int i=0;i<c;i++)
103         {
104             if(ans[i]==-1) puts("Not connected");
105             else printf("%d\n",ans[i]);
106         }
107     }
108     return 0;
109 } 
HDOJ2874(参考的AC代码)

猜你喜欢

转载自www.cnblogs.com/HDUjackyan/p/9102171.html