图论-最近公共祖先-在线树上倍增

有关概念:
  最近公共祖先(LCA,Lowest Common Ancestors):对于有根树T的两个结点u、v,最近公共祖先表示u和v的深度最大的共同祖先。

  树上倍增是求LCA的在线算法(对于每一个询问输入后即计算)
思路:
  fa[i][j]表示编号为j的结点从下往上的第2i个祖先
  即fa[0][j]表示j的父结点,递推式为fa[i][j]=fa[i-1][fa[i-1][j]],即j的第2i个祖先为j的第2i-1个祖先的第2i-1个祖先
  另:不存在第2i个祖先(即2i超过j的深度)时,f[i][j]指向根结点
  求LCA的过程分两步,都类似于逐步逼近,先使指针从深度较深的结点向上逼近直至与另一结点深度相同,再让两个指针同时向上逼近LCA,即求出结果

样例推导:

求17、13,16、6,1、15的LCA

指针1指向13,指针2指向17

i=0时,指针2移至fa[i][17],即12,使得深度与指针1相同

i=1时,fa[i][12]==fa[i][13],两指针同时移至5,LCA(17,13)=5

指针1指向6,指针2指向16

i=1时,指针2移至fa[i][16],即8,逼近指针1的深度

i=0时,指针2移至fa[i][8],即5,使得深度与指针1相同

i=0时,fa[i][5]==fa[i][6],两指针同时移至2,LCA(16,6)=2

指针1指向1,指针2指向15

i=2时,指针2移至fa[i][15],即1,使得深度与指针1相同

发现指针1和指针2重合,LCA(1,15)=1

不知大家发现没有,指针1开始一直指向深度较小的结点,这个在程序中有体现,主要是减小代码量

 1 #include<cstdio>
 2 #include<algorithm>
 3 using namespace std;
 4 struct edge
 5 {
 6     int v,next,val;
 7 }e[100005];
 8 int n,m,heads[50005],fa[17][50005],dis[50005],dep[50005],cnt;
 9 void add(int u,int v,int val)
10 {
11     e[++cnt].next=heads[u];
12     heads[u]=cnt;
13     e[cnt].v=v;
14     e[cnt].val=val;
15 }
16 void dfs(int u)//预处理dep和fa[0][j] 
17 {
18     for(int i=heads[u];i;i=e[i].next)
19     {
20         if(e[i].v!=fa[0][u])
21         {
22             dep[e[i].v]=dep[u]+1;
23             fa[0][e[i].v]=u;
24             dis[e[i].v]=dis[u]+e[i].val;
25             dfs(e[i].v);
26         }
27     }
28 }
29 int LCA(int u,int v)
30 {
31     if(dep[u]>dep[v])swap(u,v);
32     for(int i=16;~i;i--)
33         if(dep[fa[i][v]]>=dep[u])
34             v=fa[i][v];
35     if(u==v)return u;
36     for(int i=16;~i;i--)
37         if(fa[i][u]!=fa[i][v])
38         {
39             u=fa[i][u];
40             v=fa[i][v];
41         }
42     return fa[0][u];
43 }
44 int main()
45 {
46     scanf("%d",&n);
47     for(int i=1;i<n;i++)
48     {
49         int x,y,z;
50         scanf("%d%d%d",&x,&y,&z);
51         add(x,y,z);
52         add(y,x,z);
53     }
54     dep[1]=fa[0][1]=1;
55     dfs(1);
56     for(int i=1;i<=16;i++)
57         for(int j=1;j<=n;j++)
58             fa[i][j]=fa[i-1][fa[i-1][j]];
59     scanf("%d",&m);
60     while(m--)
61     {
62         int x,y;
63         scanf("%d%d",&x,&y);
64         printf("%d\n",dis[x]+dis[y]-2*dis[LCA(x,y)]);//两点之间路径长度 
65     }
66     return 0;
67 }

 

posted @ 2016-10-16 23:30  xqmmcqs  阅读(1007)  评论(0编辑  收藏  举报