最近公共祖先(LCA):倍增

来源:互联网 发布:青岛太阳软件 编辑:程序博客网 时间:2024/06/11 03:11

,https://www.luogu.org/problem/show?pid=3379
最近公共祖先方法有很多的,现在我们利用倍增表求lca
何为倍增表,简单的说是成倍增加表
bz[i][j]表示在第i位上向前推进2^j步
对于一颗有根树,bz[i][j]表示从第i为向根节点走2^j步,就是说高度增加2^j
倍增表功能强大,这里不展开(我不会…)
首先我们讲一下倍增
它的地推公示bz[i][j]=bz[bz[i][j-1]][j-1]
和st表很像的
对于一棵树,我们可以dfs求出bz[i][0]
就是i节点的父节点2^0=1;
然后

for(int j=1;n>=(1<<j);j++)        for(int i=1;i<=n;i++)            if(bz[i][j-1])                bz[i][j]=bz[bz[i][j-1]][j-1];

在爆搜的同时,我们直接求出deep[i],即深度
然后每读入两个数x,y(deep[y]>=deep[x]),如果他们有高度差,先把较深的点顺着根节点向上爬,爬到两个点相同高度;
首先我们搞一个j=0;不断增加j,使deep[bz[y][j]]>deep[x]
那么deep[bz[y][j-1]]一定小于deep[x]且距离deep[x]较近
显然因为y和bz[y][j-1]都在x的下面所以他们公共最近祖先是一样的
那我们把y提升到bz[y][j-1]的位置
继续重复,直到deep[x]==deep[y];
当然咯j不断增加,有可能bz[y][j]比根节点还大,但这时deep[bz[y][j]]是0,所以不会对答案有影响;

提升到同一高度后,我们就可以同时提升xy,方法和上面一样
直到x==y当然,每个节点本身也是自己的祖先

#include<iostream>#include<cstdlib>#include<cstdio>#include<cmath>#include<cstring>#include<algorithm>#define LL long longusing namespace std;struct cs{    int to,next;}a[2000004];int head[600005],deep[600005],bz[600001][25];int p,n,m,x,y,z,ll;void inc(int x,int y){    ll++;    a[ll].to=y;    a[ll].next=head[x];    head[x]=ll;}void dfs(int x,int y,int z){    bz[x][0]=y;    deep[x]=z;    int k=head[x];    while(k){        if(a[k].to!=y) dfs(a[k].to,x,z+1);        k=a[k].next;    }}int upone(int stdd,int x){//把两个点提升到相同高度     while(deep[x]!=stdd){        int j=0;        while(deep[bz[x][j]]>=stdd)j++;        x=bz[x][j-1];    }    return x;}int happytogether(int x,int y){//两个点一起提升,只现在更新x点,从y点过来     if(x==y)return x;    while(1){        int j=0;        if(bz[x][j]==bz[y][j])return bz[x][j];        while(bz[x][j]!=bz[y][j])j++;        j--; x=bz[x][j]; y=bz[y][j];    }}int lca(int x,int y){    if(deep[x]>deep[y])swap(x,y);    y=upone(deep[x],y);    return(happytogether(x,y));}int main(){    scanf("%d%d%d",&n,&m,&p);    for(int i=1;i<=n-1;i++){        scanf("%d%d",&x,&y);        inc(x,y);        inc(y,x);    }    dfs(p,-6,1);    for(int j=1;n>=(1<<j);j++)        for(int i=1;i<=n;i++)            if(bz[i][j-1])                bz[i][j]=bz[bz[i][j-1]][j-1];    while(m--){        scanf("%d%d",&x,&y);        printf("%d\n",lca(x,y));    }}
1 0