快速求树中某个节点的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;
}

运行结果:

在这里插入图片描述

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

nefu-ljw

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值