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

题目描述:

读入一棵以1为根的树,q次询问,每次给定x和y,问x和y的最近公共祖先是哪一个节点。

树的读入格式:n-1行每行两个整数x、y,表示一条连接x和y的边,保证输入的图形成一棵树。

输入格式:

第一行两个整数为n和q,之后按题目描述读入一棵树。

再之后q行,每行两个整数x和y,表示一组询问。

输出格式:

q行,每行1个整数表示答案。

样例输入1:

10 5
2 1
2 3
2 4
5 4
3 6
7 6
8 1
1 9
1 10
9 8
3 1
7 4
6 2
10 7

样例输出1:

1
1
2
2
1

\(1\le n,q\le 10^5\)

\(fa_{x,i}\) 表示 \(x\)\(2^i\) 级祖先,由二进制可知正确性(循环时i从大到小,详见代码),可以 \(O(n\log n)\)预处理,\(O(\log n)\)查询。

#include<bits/stdc++.h>
using namespace std;
const int N=100005;
vector<int>g[N];
int fa[N][21],n,q,dep[N];
void dfs(int u,int deep,int fat) {
    fa[u][0]=fat;
    dep[u]=deep;
    for(int i=0; i<g[u].size(); ++i) {
        int v=g[u][i];
        if(v==fat)continue;
        dfs(v,deep+1,u);
    }
}
int lca(int x,int y)
{
    if(dep[x]<dep[y])swap(x,y);
    for(int i=20;i>=0;--i)
        if(dep[fa[x][i]]>=dep[y])x=fa[x][i];
    if(x==y)return x;
    for(int i=20;i>=0;--i)
    {
        if(fa[x][i]!=fa[y][i])
        {
            x=fa[x][i];
            y=fa[y][i];
        }
    }
    return fa[x][0];
}
int main() {
    scanf("%d%d",&n,&q);
    for(int i=1,x,y;i<n;++i)
    {
        scanf("%d%d",&x,&y);
        g[x].push_back(y);
        g[y].push_back(x);
    }
    dfs(1,1,1);
    for(int i=1; i<=20; ++i) {
        for(int j=1; j<=n; ++j) {
            fa[j][i]=fa[fa[j][i-1]][i-1];
        }
    }
    while(q--)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        printf("%d\n",lca(x,y));
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/zzctommy/p/12317442.html