最近公共祖先(LCA)

倍增求LCA

\(f_{x,k}\) 表示 \(x\) 向根节点走了 \(2^k\) 步到达的节点。
易得:

\[f_{x,k+1}=f_{f_{x,k},k} \]

所以遍历一次图可预处理出。
对于一组 \((x,y)\) 的询问:

  1. 选择深度更深的节点,倍增跳到与另一点深度相同处。
  2. 若此时两点相同,则得出答案。
  3. 否则 \(x,y\) 在不重合的前提下一只向上倍增。
  4. 最后 \(x\)\(y\) 的父亲一定是 LCA。
inline void bfs(){
    t=(int)log(n)/log(2)+1;
    q.push(root);d[1]=1;
    while(q.size()){
        int x=q.front();q.pop();
        for(int i=head[x];i;i=nxt[i]){
            int y=ver[i];
            if(d[y])continue;
            d[y]=d[x]+1,f[y][x]=0,q.push(y);
            for(int j=1;j<=t;j++)f[y][j]=f[f[y][j-1]][j-1];
            q.push(y);
        }
    }
}
inline int Lca(int x,int y){
    if(dep[x]<dep[y])swap(x,y);
    for(int i=t;i>=0;i--)
        if(dep[f[x][i]]>=dep[y])x=fa[x][i];
    if(x==y)return x;
    for(int i=t;i>=0;i--)
        if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
    return f[x][0];
}

P4180 [BJWC2010]严格次小生成树
容易得到,严格次小生成树一定是最小生成树替换一条边得到的。
先求出最小生成树,考虑加入未在最小生成树内的边 \((x,y,z)\),那么树上 \(x\)\(y\) 的路径上一定有一条边被替换。
\(x\)\(y\) 的路径上最小边权为 \(z_1\),严格次小边权为 \(z_2\), 那么:

  1. \(z\ne z_1\) 时,替换 \((x,y,z)\) 得到的严格次小生成树一定换去 \(z_1\) 这条边。
  2. 否则换去 \(z_2\) 这条边。

用树上倍增求出 \(z_1\)\(z_2\),此过程和求 LCA 相似。

P4281 [AHOI2008]紧急集合 / 聚会
建议画图理解。
引理1:三个点的两两 LCA 必有一对重合。
反证法易证。
引理2:答案一定是另一个 LCA。
如果选另一个LCA,两个 LCA 中间的边被经过1次,否则被经过2次。

P1852 跳跳棋

由于一种状态想要缩小只有一种方案,所以可以把缩小的方案建成一棵树,答案就是起始和结束状态的 LCA。
考虑不暴力缩小,而是一次把一边缩小到比另一边小。
最后二分距离 LCA 的距离后倍增即可。

posted @ 2021-11-14 19:35  字如其人  阅读(67)  评论(0)    收藏  举报