题目链接:https://codeforces.com/contest/208/problem/E
题意:n个节点,组成森林。m个询问,一个询问由(v,p)组成,问v的p-cousin有多少,v的p-cousin(v1)指v,v1在同一层且他们和LCA(v,v1)距离为p。
思路:将询问转化为子树上,对于一个询问(v,p),我们从下往上找v的第p个祖先(u)[我用的倍增],那么询问的答案就是问u的子树中,深度为deep[u]+p节点的个数-1。维护每个深度有多少节点即可。
代码:
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e5+10;
int n,m;
struct edge{
int to,nex;
}g[N];
int head[N],cnt=0;
struct node{
int id,dep,nex;
}q[N];
int head1[N],cnt1=0;
bool book[N];
void add(int u,int v){
g[cnt]=edge{v,head[u]};
head[u]=cnt++;
}
int sz[N],son[N],dep[N],f[N][25];
void getsz(int u){
int mx=-1;
sz[u]=1;
for(int i=1;(1<<i)<=n;i++)
f[u][i]=f[f[u][i-1]][i-1];
for(int i=head[u];~i;i=g[i].nex){
int v=g[i].to;
f[v][0]=u;
dep[v]=dep[u]+1;
getsz(v);
sz[u]+=sz[v];
if(sz[v]>mx){
son[u]=v;
mx=sz[v];
}
}
}
int getf(int u,int dis){
for(int i=20;i>=0;i--)
if(dis&(1<<i))
u=f[u][i];
return u;
}
void add1(int u,int id,int de){
q[cnt1]=node{id,de,head1[u]};
head1[u]=cnt1++;
}
int num[N],SON;
void dfs1(int u,int x){
num[dep[u]]+=x;
for(int i=head[u];~i;i=g[i].nex){
int v=g[i].to;
if(v==SON) continue;
dfs1(v,x);
}
}
int ans[N];
void dfs(int u,bool keep){
for(int i=head[u];~i;i=g[i].nex){
int v=g[i].to;
if(v==son[u]) continue;
dfs(v,0);
}
if(son[u]!=-1){
dfs(son[u],1);
SON=son[u];
}
dfs1(u,1);
SON=0;
for(int i=head1[u];~i;i=q[i].nex)
ans[q[i].id]=num[q[i].dep]-1;
if(!keep) dfs1(u,-1);
}
int main(void){
scanf("%d",&n);
for(int i=1;i<=n;i++)
son[i]=head1[i]=head[i]=-1;
for(int i=1;i<=n;i++){
int u;
scanf("%d",&u);
if(u)
add(u,i);
book[i]=(u==0);
}
for(int i=1;i<=n;i++)
if(book[i]){
dep[i]=1;
getsz(i);
}
scanf("%d",&m);
for(int i=1;i<=m;i++){
int u,k;
scanf("%d%d",&u,&k);
if(dep[u]>k){
int x=getf(u,k);
add1(x,i,dep[u]);
}
}
for(int i=1;i<=n;i++)
if(book[i])
dfs(i,0);
for(int i=1;i<=m;i++)
printf("%d%c",ans[i],i==m?'\n':' ');
return 0;
}