最近公共祖先(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 }

 

 

 

 

posted @ 2014-12-24 23:42  MedalPluS  阅读(259)  评论(0编辑  收藏  举报