倍增 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 }
View Code

 

 

 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 }
View Code

 


以上是关于倍增 Tarjan 求LCA的主要内容,如果未能解决你的问题,请参考以下文章

两种lca的求法:树上倍增,tarjan

Tarjan求LCA

[luogu3379]最近公共祖先(树上倍增求LCA)

lca

LCA--倍增法

hdu 2586 How far away ?倍增LCA