题目链接:
题意:
给你一棵树,1为根节点,在第1层,q次询问;
每次询问输入两个数,u,d;
要求计算出在树的第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;
}