CodeForces 208 E.Blood Cousins (树上倍增+dsu on tree)

题意:

给n个点的树,定义从x点向上走k步到达的节点为x的 k层祖先。
m次查询,给出x k,问有多少个点与x具有相同的 k层祖先。

思路:

先求出x的k层祖先fa,题目转化为求以fa为根的子树下深度与x相同的节点个数,减去x本身的一个就是答案了

x的k层祖先可以用树上倍增解决

离线储存询问,问题就变成离线子树问题,用dsu on tree解决,

询问储存的方式是:二元组存储问题id和x的层数,
每当统计完根为fa的子树之后,遍历以fa为根的询问,然后更新答案,具体看代码

cal函数计算的是以当前点为根的子树中,各个深度的点的个数

code:

#include<bits/stdc++.h>
using namespace std;
const int maxm=1e5+5;
const int maxd=15;
vector<int>g[maxm];
vector<pair<int,int> >q[maxm];
int f[maxm][20],d[maxm];
int sz[maxm],son[maxm];
int mark[maxm];
int cnt[maxm];
int res[maxm];
int n,m;
void dfs(int x,int fa){
    //倍增
    for(int i=1;i<=maxd;i++){
        if(!f[x][i-1])break;
        f[x][i]=f[f[x][i-1]][i-1];
    }
    //树形dp求轻重儿子
    sz[x]=1;
    son[x]=-1;
    for(int v:g[x]){
        if(v==fa)continue;
        f[v][0]=x;
        d[v]=d[x]+1;
        dfs(v,x);
        sz[x]+=sz[v];
        if(son[x]==-1||sz[son[x]]<sz[v]){
            son[x]=v;
        }
    }
}
int getfa(int x,int dep){//求深度为dep的x的祖先
    for(int i=maxd;i>=0;i--){
        if(d[f[x][i]]>=dep){
            x=f[x][i];
        }
    }
    return x;
}
void cal(int x,int fa,int change){
    cnt[d[x]]+=change;
    for(int v:g[x]){
        if(v==fa||mark[v])continue;
        cal(v,x,change);
    }
}
void solve(int x,int fa,int kep){
    for(int v:g[x]){//先解决轻儿子
        if(v==fa||v==son[x])continue;
        solve(v,x,0);
    }
    if(son[x]!=-1){//再解决重儿子
        solve(son[x],x,1);
        mark[son[x]]=1;
    }
    cal(x,fa,1);//计算轻儿子
    for(auto i:q[x]){//遍历以x为祖先的询问,更新询问的答案
        res[i.first]=cnt[i.second]-1;
    }
    if(son[x]!=-1){
        mark[son[x]]=0;
    }
    if(!kep){
        cal(x,fa,-1);
    }
}
signed main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        int x;
        scanf("%d",&x);
        g[i].push_back(x);
        g[x].push_back(i);
    }
    dfs(0,0);
    scanf("%d",&m);
    for(int i=1;i<=m;i++){
        int x,k;
        scanf("%d%d",&x,&k);
        if(d[x]-k<=0){//如果向上走越界了
            res[i]=0;
            continue;
        }
        int fa=getfa(x,d[x]-k);//求祖先
        q[fa].push_back(make_pair(i,d[x]));//询问id和深度d[x]的二元组询问存入q[fa]中
    }
    solve(0,0,1);
    for(int i=1;i<=m;i++){
        printf("%d ",res[i]);
    }
    puts("");
    return 0;
}
发布了364 篇原创文章 · 获赞 26 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_44178736/article/details/103170238