倍增 Tarjan 求LCA
Posted forward777
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了倍增 Tarjan 求LCA相关的知识,希望对你有一定的参考价值。
----代码都是 HDU 2586 "How far away" 为例
倍增求LCA
树上倍增法。
设F[x,k] 表示x的2的k次方辈祖先,即 由x向上走2的k次方到达的节点
F[x,k]=F[F[x][k-1],k-1]
预处理: 这类似于一个动态规划的过程,阶段就是节点的深度,因此,我们可以对树进行bfs,按照层次顺序,下节点入队之前,计算它在F数组中相应的值。
基于F数组计算LCA:
1.设d[x]表示x的深度。不妨设d[x]>d[y]
2.用二进制拆分思想,把x上调到与y同一深度
3.若x=y则LCA=y
否则 把x,y同时向上调整,并保持深度一致且二者不相会。
具体来说,就是依次尝试把x,y同时向上走2的整数次方步(递减),在每此尝试中,若F[x,k]!=F[y,k],则令x=F[x,k],y=F[y,k]
4.此时x,y必定只差一步就相会了 则 LCA=fa[x] (x的父结点)
-----《算法竞赛》
1 #include<iostream> 2 #include<cstdio> 3 #include<vector> 4 #include<cmath> 5 #define M 40010 6 #define go(i,a,b) for(register int i=a;i<=b;i++) 7 #define go1(i,a,b) for(register int i=a;i>=b;i--) 8 using namespace std; 9 int read() 10 { 11 int x=0,y=1; char c; c=getchar(); 12 while(c<‘0‘||c>‘9‘) {if(c==‘-‘) y=-1;c=getchar();} 13 while(c>=‘0‘&&c<=‘9‘) {x=(x<<3)+(x<<1)+c-‘0‘;c=getchar();} 14 return x*y; 15 } 16 int b[M],d[M],f[M][30],dis[M],T,m,n,tt,t; 17 struct node1 {int v,w,n;} a[M*2]; 18 void add(int u,int v,int w) {a[++tt].n=b[u];a[tt].v=v;a[tt].w=w;b[u]=tt;} 19 void dfs(int u) 20 { 21 int v,w; 22 for(int i=b[u];i;i=a[i].n) 23 { 24 v=a[i].v;w=a[i].w; 25 if(v==f[u][0]) continue ; 26 f[v][0]=u; 27 dis[v]=dis[u]+w; 28 d[v]=d[u]+1; 29 go(j,1,t) f[v][j]=f[f[v][j-1]][j-1]; 30 dfs(v); 31 } 32 } 33 int lca(int u,int v) 34 { 35 if(d[u]>d[v]) swap(u,v); 36 go1(i,t,0) if(d[f[v][i]]>=d[u]) v=f[v][i]; 37 if(u==v) return u; 38 go1(i,t,1) if(f[u][i]!=f[v][i]) {u=f[u][i];v=f[v][i];} 39 return f[u][0]; 40 } 41 int main() 42 { 43 T=read(); 44 while(T--) 45 { 46 int u,v,w; 47 n=read();m=read();t=(int)(log(n)/log(2))+1; 48 tt=0; go(i,1,n) {dis[i]=0;d[i]=0;b[i]=0;} 49 go(i,1,n-1) {u=read();v=read();w=read();add(u,v,w);add(v,u,w);} 50 d[1]=1;dfs(1); 51 go(i,1,m) 52 {u=read();v=read();printf("%d ",dis[u]+dis[v]-2*dis[lca(u,v)]);} 53 } 54 return 0; 55 }
Tarjan求LCA
算法本质是使用并查集对“向上标记法”的优化。离线算法。复杂度为O(m+n)。
-----《算法竞赛》
画图非常清晰明了啊
1 #include<iostream> 2 #include<cstdio> 3 #include<vector> 4 #define M 40010 5 #define go(i,a,b) for(register int i=a;i<=b;i++) 6 using namespace std; 7 int read() 8 { 9 int x=0,y=1; char c; c=getchar(); 10 while(c<‘0‘||c>‘9‘) {if(c==‘-‘) y=-1;c=getchar();} 11 while(c>=‘0‘&&c<=‘9‘) {x=(x<<3)+(x<<1)+c-‘0‘;c=getchar();} 12 return x*y; 13 } 14 vector<int> q[M][2]; 15 int b[M],ans[M],dis[M],f[M],fa[M]; 16 int tt,n,m,T; 17 struct node1 {int v,w,n;} a[M*2]; 18 void add(int u,int v,int w) {a[++tt].n=b[u];a[tt].v=v;a[tt].w=w;b[u]=tt;} 19 int find(int x) {if(fa[x]==x) return x; return fa[x]=find(fa[x]);} 20 void tarjan(int u) 21 { 22 f[u]=1;int v,w,id; 23 for(int i=b[u];i;i=a[i].n) 24 { 25 v=a[i].v;w=a[i].w; 26 if(f[v]) continue ; 27 dis[v]=dis[u]+w; 28 tarjan(v); 29 fa[v]=u; 30 } 31 for(int i=0;i<q[u][0].size();i++) 32 { 33 v=q[u][0][i];id=q[u][1][i]; 34 if(f[v]==2) 35 {ans[id]=dis[u]+dis[v]-2*dis[find(v)];} 36 } 37 f[u]=2; 38 } 39 void init() 40 { 41 tt=0; 42 go(i,1,n) 43 { 44 fa[i]=i;q[i][1].clear();q[i][2].clear();dis[i]=0;f[i]=0;b[i]=0; 45 } 46 } 47 int main() 48 { 49 T=read(); 50 while(T--) 51 { 52 n=read();m=read(); int u,v,w; 53 init();; 54 go(i,1,n-1) 55 {u=read();v=read();w=read();add(u,v,w);add(v,u,w);} 56 go(i,1,m) 57 { 58 u=read();v=read(); 59 q[u][0].push_back(v);q[v][0].push_back(u); 60 q[u][1].push_back(i);q[v][1].push_back(i); 61 } 62 tarjan(1); 63 go(i,1,m) printf("%d ",ans[i]); 64 } 65 return 0; 66 }
以上是关于倍增 Tarjan 求LCA的主要内容,如果未能解决你的问题,请参考以下文章