题意:给你一片森林,每次询问一个点与多少个点拥有共同的(K)级祖先.(n,m<=100000.)
分析:先不管森林不森林的(森林就当做多棵独立的树来处理就好了,反正产生的贡献相互不影响),在一棵树上,我们要维护每个点有多少个点的深度与之相同,空间是开不下的.所以我们需要稍微转化一下,维护每个点的深度为(K)的儿子有多少个.所以我们先通过(LCA)找到节点(x)的(K)级祖先(y),然后把((y,dep[u]))当做一组询问,表示要查询(y)的子树中深度为(dep[u])的儿子的数量.
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define ll long long
using namespace std;
inline int read(){
int x=0,o=1;char ch=getchar();
while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
if(ch=='-')o=-1,ch=getchar();
while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
return x*o;
}
const int N=1e5+5;
int n,m;
int f[N][20],visit[N],sum[N],ans[N];
int dep[N],root[N],size[N],son[N];
int tot,head[N],nxt[N],to[N],Head[N];
inline void add(int a,int b){nxt[++tot]=head[a];head[a]=tot;to[tot]=b;}
struct query{int dep,nxt;}a[N];
void Add(int x,int d,int id){a[id]=(query){d,Head[x]};Head[x]=id;}
inline void pre_dfs(int u){
size[u]=1;
for(int j=1;j<=18;++j)f[u][j]=f[f[u][j-1]][j-1];
for(int i=head[u];i;i=nxt[i]){
int v=to[i];dep[v]=dep[u]+1;f[v][0]=u;
pre_dfs(v);size[u]+=size[v];
if(size[v]>size[son[u]])son[u]=v;
}
}
inline void update(int u,int val){
sum[dep[u]]+=val;
for(int i=head[u];i;i=nxt[i]){
int v=to[i];if(visit[v])continue;
update(v,val);
}
}
inline void dfs(int u,int keep){
for(int i=head[u];i;i=nxt[i]){
int v=to[i];if(v==son[u])continue;
dfs(v,0);
}
if(son[u])dfs(son[u],1),visit[son[u]]=1;update(u,1);
for(int i=Head[u];i;i=a[i].nxt)ans[i]=sum[a[i].dep]-1;
visit[son[u]]=0;if(!keep)update(u,-1);
}
int main(){
n=read();
for(int i=1;i<=n;++i){
int x=read();
if(!x)root[++root[0]]=i,dep[i]=1;
else add(x,i);
}
for(int i=1;i<=root[0];++i)pre_dfs(root[i]);
m=read();
for(int i=1;i<=m;++i){
int x=read(),k=read(),d=dep[x];
for(int j=0;j<=18;++j)if(k&(1<<j))x=f[x][j];
Add(x,d,i);//转化询问方式
}
for(int i=1;i<=root[0];++i)dfs(root[i],0);
//我写上面分析森林的时候理解了为什么本题中keep要初始化为0
//因为每棵树是互相独立的,所以每棵树产生的贡献都要被清除
for(int i=1;i<=m;++i)printf("%d
",ans[i]);
return 0;
}