LCA回顾(主要针对倍增算法)

这个有两种方法:

一、倍增

就是维护一个节点的第\(2^0,\ 2^1,\ 2^2\ ...\)层父亲,这样的话我们在后面查询的时候就可以直接“跳”着找。

void pushup()
{
    step=log(maxd*1.0)/log(2.0);

    for(int i=1;i<=step;i++)
        for(int p=1;p<=n;p++) if( fa[p][i-1] ) fa[p][i]=fa[ fa[p][i-1] ][i-1];

    return;
}

然后查询的时候,先让两个点跳到同一深度

所以要让较深的一个点先往上跳:

if( dep[x]<dep[y] ) swap(x,y);
for(int i=step;i>=0;i--) if( dep[x]-(1<<i)>=dep[y] ) x=fa[x][i];

第二行相当于将\(dep(x)-dep(y)\)的值做二进制拆分


跳到同一深度之后,如果两个节点已经跳到同一个位置,就直接退出。

否则,就要继续同时往上跳,但是,在跳的时候要保证:它们各自跳到的位置必须是不一样的!不然的话它们可能都跳到了\(LCA\)的上面!

整个功能段见下(省略了建图部分):

struct LCA
{
    int fa[maxn][22],dep[maxn];
    int maxd;
    
    void dfs(int x,int f,int d)
    {
        fa[x][0]=f,dep[x]=d;
        maxd=max( maxd,d );
        
        for(int i=G.Head[x];i;i=G.Next[i])
        {
            Graph::Edge e=G.edges[i];
            if( e.to==f ) continue;
            
            dfs(e.to,x,d+1);
        }
        
        return;
    }
    
    int step;
    
    void pushup()
    {
        step=log(maxd*1.0)/log(2.0);
        
        for(int i=1;i<=step;i++)
            for(int p=1;p<=n;p++) if( fa[p][i-1] ) fa[p][i]=fa[ fa[p][i-1] ][i-1];
        
        return;
    }
    
    int LCA(int x,int y)
    {
        if( dep[x]<dep[y] ) swap(x,y);
        
        for(int i=step;i>=0;i--) if( dep[x]-(1<<i)>=dep[y] ) x=fa[x][i];
        if( x==y ) return y;
        
        for(int i=step;i>=0;i--)
            if( fa[x][i]!=fa[y][i] ) x=fa[x][i],y=fa[y][i];
        
        return fa[x][0];
    }
}SOL;

二、树链剖分

这个更简单,用\(top\)数组直接跳就行了……

猜你喜欢

转载自www.cnblogs.com/info---tion/p/11282987.html