Count Descendants--长链剖分

题目链接:

E - Count Descendants

题意:

给你一棵树,1为根节点,在第1层,q次询问;
每次询问输入两个数,ud
要求计算出在树的第d+1层并且是节点u的子孙节点节点个数

思路:

读完题很明显是长链剖分板子题,求某节点对应子树第k层的信息。

代码:

#include <bits/stdc++.h>
 
typedef long long ll;
using namespace std;
 
const int N=2e5+10;
int h[N],to[2*N],ne[2*N],cnt;
int dep[N],fa[N],son[N],len[N];
//用len数组用于保存长链的长度 
int top[N],dfn,L[N],R[N],idx[N],skp;
int ans[N],sum[N*2];
int n,m;
vector<pair<int,int>>lis[N];
void add_edge(int u,int v){
	to[cnt]=v;
	ne[cnt]=h[u];
	h[u]=cnt++;
} 
void init(){
	cnt=0;
	memset(h,-1,sizeof(h));
}
void dfs1(int u,int fat){
	dep[u]=dep[fat]+1;
	fa[u]=fat;
	for(int i=h[u];i!=-1;i=ne[i]){
		if(to[i]==fat)
			continue;
		dfs1(to[i],u);
		if(!son[u]||len[son[u]]<len[to[i]])
			son[u]=to[i];
	}
	len[u]=len[son[u]]+1;
}

void dfs2(int u){
	L[u]=++dfn;
	R[u]=dfn+len[u]-1;
	idx[dfn]=u;
	//idx数组可有可无,不需要清空数据结构了,top数组也可有可无 
	if(son[u])
		dfs2(son[u]);
	for(int i=h[u];i!=-1;i=ne[i]){
		if(to[i]!=fa[u]&&to[i]!=son[u]){
			dfs2(to[i]);
		}
	}
}
void dsu(int u){
	//1.处理重儿子 
	if(son[u])
		dsu(son[u]);
	//2.处理轻儿子并同时把轻儿子往重儿子上并 
	for(int i=h[u];i!=-1;i=ne[i]){
		if(to[i]==fa[u]||to[i]==son[u])
			continue;
		dsu(to[i]); 
		//轻儿子的起始点是对应u深度为1的点 
		for(int j=L[to[i]],k=1;j<=R[to[i]];j++,k++){
			sum[L[u]+k]+=sum[j];
		}
	}
	//加上自己 
	sum[L[u]]+=1;
	//3.计算答案 
	for(auto &i:lis[u]){
		if(L[u]+i.second>R[u]){
    		ans[i.first]=0;
		}
		else
			ans[i.first]=sum[L[u]+i.second];
	}
	return;
} 
int main(){
	init();
    scanf("%d",&n);
    int u,v; 
    for(int i=2;i<=n;i++){
        scanf("%d",&u);
        v=i;
        add_edge(u,v);
        add_edge(v,u);
    }
    dfs1(1,0);
    dfs2(1);
    scanf("%d",&m);
    int x,y;
    for(int i=1;i<=m;++i){
        scanf("%d %d",&x,&y);
        //以x为子节点深度为y的和 
        y=y-dep[x]+1;
        if(y<0)
        	ans[i]=0;
        else
        	lis[x].push_back(make_pair(i,y));
    }
    dsu(1);
    for(int i=1;i<=m;++i){
        printf("%d\n",ans[i]);
    }
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值