专题训练之LCA

Posted jackyan

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了专题训练之LCA相关的知识,希望对你有一定的参考价值。

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

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

http://www.cnblogs.com/zhouzhendong/p/7256007.html 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

ST+DFS在线算法

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 using namespace std;
  5 const int maxn=10010;
  6 int rmq[maxn*2]; //即欧拉序列对应的深度序列 
  7 struct ST
  8 {
  9     int mm[2*maxn];
 10     int dp[2*maxn][20]; //最小值对应的下标
 11     void init(int n)
 12     {
 13         mm[0]=-1;
 14         for ( int i=1;i<=n;i++ )
 15         {
 16             mm[i]=((i&(i-1))==0)?mm[i-1]+1:mm[i-1];
 17             dp[i][0]=i;
 18         }
 19         for ( int j=1;j<=mm[n];j++ )
 20         {
 21             for ( int i=1;i+(1<<j)-1<=n;i++ ) 
 22             {
 23                 if ( rmq[dp[i][j-1]]<rmq[dp[i+(1<<(j-1))][j-1]] ) dp[i][j]=dp[i][j-1];
 24                 else dp[i][j]=dp[i+(1<<(j-1))][j-1];    
 25             }
 26         }
 27     } 
 28     int query(int a,int b) //查询[a,b] 之间最小值的下标 
 29     {
 30         if ( a>b ) swap(a,b);
 31         int k=mm[b-a+1];
 32         if ( rmq[dp[a][k]]<=rmq[dp[b-(1<<k)+1][k]] ) return dp[a][k];
 33         else return dp[b-(1<<k)][k];
 34     }
 35 };
 36 struct Edge{
 37     int to,nxt;
 38 }edge[maxn*2];
 39 int tot,head[maxn];
 40 int F[maxn],P[maxn],cnt; //F为欧拉序(即DFS遍历的顺序),长度为2*n-1,从1开始;P为所有点第一次在F中出现的位置 
 41 ST st;
 42 
 43 void init()
 44 {
 45     tot=0;
 46     memset(head,-1,sizeof(head));
 47 }
 48 
 49 void addedge(int u,int v) //加边,无向边需要加两次 
 50 {
 51     edge[tot].to=v;
 52     edge[tot].nxt=head[u];
 53     head[u]=tot++;
 54 }
 55 
 56 void dfs(int u,int pre,int dep)
 57 {
 58     F[++cnt]=u;
 59     rmq[cnt]=dep;
 60     P[u]=cnt;
 61     for ( int i=head[u];i!=-1;i=edge[i].nxt )
 62     {
 63         int v=edge[i].to;
 64         if ( v==pre ) continue;
 65         dfs(v,u,dep+1);
 66         F[++cnt]=u;
 67         rmq[cnt]=dep;
 68     }
 69 }
 70 
 71 void LCA_init(int root,int num) //查询LCA前的初始化 
 72 {
 73     cnt=0;
 74     dfs(root,root,0);
 75     st.init(2*num-1);
 76 }
 77 
 78 int query(int u,int v) //查询LCA(u,v)的编号 
 79 {
 80     return F[st.query(P[u],P[v])];
 81 }
 82 bool flag[maxn];
 83 
 84 int main()
 85 {
 86     int T,N,u,v;
 87     scanf("%d",&T);
 88     while ( T-- )
 89     {
 90         scanf("%d",&N);
 91         init();
 92         memset(flag,false,sizeof(flag));
 93         for ( int i=1;i<N;i++ )
 94         {
 95             scanf("%d%d",&u,&v);
 96             addedge(u,v);
 97             addedge(v,u);
 98             flag[v]=true;
 99         }
100         int root;
101         for ( int i=1;i<=N;i++ )
102         {
103             if ( !flag[i] ) 
104             {
105                 root=i;
106                 break;
107             }
108         }
109         LCA_init(root,N);
110         scanf("%d%d",&u,&v);
111         printf("%d\\n",query(u,v));
112     }
113     return 0;
114 }
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];
面试专题训练之“双指针”

专题训练之主席树

专题训练之双连通

专题训练之Trie

专题训练之莫队算法

专题训练之数位DP