专题训练之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 }
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 }
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 }
练习题:
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]; 面试专题训练之“双指针”