快速求树中某个节点的k级祖先【倍增/LCA魔改】

题目描述

Time Limit: 12000 MS Memory Limit: 131072 K

Description
你将得到一个有根树,其中根节点的位置是已知的。

其后将有多个询问,每个询问包括两个属性x和k,要求你找到x的第k级祖先,如果不存在,则输出“0”。

假设有一棵树形如下图:
在这里插入图片描述

其中,树的根节点为绿色点。

若x为红点,则其二级祖先是蓝色点,三级祖先是绿色点。

Input
本题为多组数据输入。
每组数据的第一行包含两个数字n和t,代表一个以节点t为根的树,这棵树的节点共有n个(下标为1-n)。(1 <= n <= 1000000)
接下来n-1行,每行两个数字u和v,代表节点u与节点v之间有一条无向边。
接下来一行有一个整数Q,代表询问的次数。(1 <= Q <= 1000000)
接下来Q行,每行有两个数字x和k,代表你要查询节点x的k级祖先。
题目保证n的总和不超过2000000。

Output
对于每组询问,输出一个整数代表你所找到的k级祖先的节点下标。
如果找不到,则输出0。

Sample Input
7 1
1 2
1 3
2 4
2 5
4 6
4 7
4
6 3
1 2
7 2
4 1

Sample Output
1
0
2
2

AC代码

可以说是LCA的简易版本 (魔改LCA模板) ,不过我LCA忘得差不多了…正好好好复习一下…

#include <bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int n,m,x,y,k,s,cnt,f[N][21],dep[N],head[N];
//f[i][j]表示i的2^j祖先,也就是i向上跳2^j步的点。dep表示深度。
struct node
{
    int to,next;
}e[N<<1];
void add(int x,int y)
{
    e[cnt].to=y;
    e[cnt].next=head[x];
    head[x]=cnt++;
}
void dfs(int u,int father)//预处理得到f数组,u表示当前搜索到的点,father表示u的父节点
{
    dep[u]=dep[father]+1;//u的父节点深度+1就是u的深度
    f[u][0]=father;//u向上跳2^0步(1步),为father点
    for(int i=1;i<=20;i++)//u与其祖先的距离最大不超过2^20
        f[u][i]=f[f[u][i-1]][i-1];//dp的思想:u向上跳2^i步相当于u向上跳2^(i-1)步,再向上跳2^(i-1)步
    for(int i=head[u];i!=-1;i=e[i].next)//遍历与u相连的点
    {
        int v=e[i].to;
        if(v!=father)dfs(v,u);//如果当前遍历的点v不是father点,则可以向下继续搜索
    }
}
int get_fa(int x,int k)
{
    int t=dep[x]-k;
    for(int i=20;i>=0;i--)//倍增向上跳跃,求祖先
        if(dep[f[x][i]]>t)x=f[x][i];
    return f[x][0];
}
int main()
{
    ios::sync_with_stdio(false);
    while(cin>>n>>s)
    {
        memset(head,-1,sizeof(head));
        for(int i=1;i<=n-1;i++)
        {
            cin>>x>>y;
            add(x,y);
            add(y,x);
        }
        memset(f,0,sizeof(f));
        dfs(s,0);//从起点s开始搜索,设起点的父节点为0
        cin>>m;
        while(m--)
        {
            cin>>x>>k;
            printf("%d\n",get_fa(x,k));
        }
    }
    return 0;
}

运行结果:

在这里插入图片描述

发布了72 篇原创文章 · 获赞 91 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/ljw_study_in_CSDN/article/details/103638883