首页 > 代码库 > 最近公共祖先(LCA)

最近公共祖先(LCA)

最近公共祖先(LCA)

by mps

  Define:求树上两个点的祖先中里两个点最近的一个点,该点称为这两个点的最近公共祖先(英译LCA<Lowest Common Ancestors>)。

  那么,如何求LCA呢?

  经过思考,不难发现,有一种暴力方法,我们对于这两个点不断BFS,直到出现一个相同的点,该点即为LCA,空间如果跟不上的话可以改为迭代加深搜索

  时间复杂度O(N2),对于大一点的数据,会TLE的,我们追求完美,还有更好的算法。

算法改进

   我们可以先求出两个节点在这颗树中的深度(D1,D2),然后由偏低的上升至偏高的,在一起走,直到出现重合,重合点为LCA

   为什么这种算法可行呢?显然可以发现想要走到共同点,需要高度一样,所以就有了求深度。一起走所消耗的时间最少(这不用证明了吧?)

   求深度DFS即可,时间复杂度O(NlogN),加上每次在线求LCA的时间为O(logN),所以该算法的时间复杂度为(NlogN+QlogN)  (Q为询问次数)

   怎么一起走?怎么上升?

    我们可以设p(i,j)来表示第i个节点向上走2^j步的祖先,那样我们就可以不断倍增,直到高度≥初始偏高的点,然后一起倍增,直到倍增的点一样,即该祖先为

    LCA

    应用实例

    货车运输(NOIP2013提高组Day1 T3)

【问题描述】
A国有 n 座城市,编号从1 到n,城市之间有 m条双向道路。每一条道路对车辆都有重量限制,简称限重。
现在有 q 辆货车在运输货物,司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物。
【输入】
输入文件名为 truck.in。
输入文件第一行有两个用一个空格隔开的整数 n,m,表示 A 国有 n 座城市和 m 条道路。
接下来 m 行每行 3 个整数 x、y、z,每两个整数之间用一个空格隔开,表示从 x 号城市到 y号城市有一
条限重为 z 的道路。注意:x 不等于 y,两座城市之间可能有多条道路。
接下来一行有一个整数 q,表示有 q 辆货车需要运货。
接下来 q 行,每行两个整数 x、y,之间用一个空格隔开,表示一辆货车需要从 x 城市运输货物到 y 城市
注意:x 不等于 y。
【输出】
输出文件名为 truck.out。
输出共有 q 行,每行一个整数,表示对于每一辆货车,它的最大载重是多少。如果货车不能到达目的地,输
出-1
【输入输出样例】
truck.in truck.out
4 3       3
1 2 4     -1
2 3 3     3
3 1 1
3
1 3
1 4
1 3

【数据范围】

对于60%的数据,n<1,000

对于100%的数据,n<10,000,m<50,000

【分析】

  一开始拿到这题以为dinic+最大流→_→,看到数据就心灰意冷了,承受不了,我们来简单分析一下:

  题目要求我们找到两个点最大边的最小权值,很明显除了最大连通边以外的边全是无用的,那么我们

  可以使用最大生成树来删掉多余的边(说的严谨一点,就是保留最大边),然后在这颗树上寻找两点

  的之间的边的最小权值,注意到了“最小”,而且时间要求很严,这时我们想到了LCA,两个点的边

  的最小权值边不就是他们任一点的到他们的LCA的边中的最小的吗?而且LCA时间消耗还正好符合本题

  所以采用LCA(树上倍增)+MST+DP来解决这题

【代码】

  

  1 #include <iostream>  2 #include <cstdio>  3 #include <algorithm>  4 #include <cstring>  5 using namespace std;  6   7 const int MaxM=108888;  8 const int MaxN=10888;  9  10 int n,m,q; 11  12 struct data{ 13     int u,v,w; 14     friend bool operator< (data a,data b){return a.w>b.w;}//BST 15 }a[MaxM]; 16  17 struct list{ 18     int to,next,w; 19 }e[MaxM]; 20  21 int head[MaxN],cnt=0; 22 int t[MaxN],deep[MaxN],p[MaxN][21],d[MaxN][21]; 23 //PS:p(i,j)表示i的第2^j的祖先,d(i,j)表示i走到2^j的祖先的路途中最小权边 24   25 void addedge(int u,int v,int w){ 26     cnt++; 27     e[cnt].to=v; 28     e[cnt].next=head[u]; 29     e[cnt].w=w; 30     head[u]=cnt; 31 } 32  33 void init(){ 34     freopen("truck.in","r",stdin); 35     freopen("truck.out","w",stdout); 36     scanf("%d%d",&n,&m); 37     int i; 38     for(i=1;i<=m;i++)scanf("%d%d%d",&a[i].u,&a[i].v,&a[i].w); 39     memset(t,-1,sizeof(t)); 40     memset(deep,0,sizeof(deep)); 41 } 42  43 int findR(int x){return (t[x]==-1?x:t[x]=findR(t[x]));} 44  45 void Kruskal(){ 46     int X,Y,i=1,k=0; 47     sort(a+1,a+m+1); 48     while(k<n-1 && i<=m){ 49         X=findR(a[i].u); 50         Y=findR(a[i].v); 51         if(X!=Y){ 52             t[X]=Y; 53             addedge(a[i].u,a[i].v,a[i].w); 54             addedge(a[i].v,a[i].u,a[i].w); 55             k++; 56         } 57         i++; 58     } 59 } 60  61 void dfs(int u){ 62     int i; 63     for(i=1;i<=16;i++) 64     { 65       if(deep[u]<(1<<i))break; 66       p[u][i]=p[p[u][i-1]][i-1]; 67       d[u][i]=min(d[u][i-1],d[p[u][i-1]][i-1]); 68     } 69     for(i=head[u];i;i=e[i].next) 70       if(!deep[e[i].to]){ 71           deep[e[i].to]=deep[u]+1; 72           p[e[i].to][0]=u; 73           d[e[i].to][0]=e[i].w; 74           dfs(e[i].to); 75       } 76 } 77  78 int lca(int u,int v){ 79     if(deep[u]<deep[v])swap(u,v); 80     int c=deep[u]-deep[v],i; 81     for(i=0;i<=16;i++) 82       if((1<<i)&c)u=p[u][i]; 83     for(i=16;i>=0;i--) 84       if(p[u][i]!=p[v][i]){ 85           u=p[u][i]; 86           v=p[v][i]; 87       } 88     if(u==v)return u; 89     else return p[u][0]; 90 } 91  92 int query(int u,int v){ 93     int i,res=0x3f3f3f3f,c=deep[u]-deep[v]; 94     for(i=0;i<=16;i++) 95       if(c&(1<<i)){ 96           res=min(res,d[u][i]); 97           u=p[u][i]; 98       } 99     return res;100 }101 102 void solve(){103     Kruskal();104     int i,j;105     for(i=1;i<=n;i++)106       if(!deep[i]){107         p[i][0]=i;108         d[i][0]=0x3f3f3f3f;109         deep[i]=1;110         dfs(i);111       }112     scanf("%d",&q);113     int top,u,v;114     while(q--){115         scanf("%d%d",&u,&v);116         if(findR(u)!=findR(v))printf("-1\n");117         else {118             top=lca(u,v);119             printf("%d\n",min(query(u,top),query(v,top)));120         }121     }122 }123 124 int main(){125     init();126     solve();127     return 0;128 }

 

 

 

 

最近公共祖先(LCA)