题意
给你一片森林,每次询问一个点与多少个点拥有共同的K级祖先
当我们遍历完 u u u的子树时,应该解决所有和 u u u子树相关的询问
所以需要转化问题
就是对于一个询问点 u u u和 k k k级祖先
直接把询问加到 k k k级祖先上去,这样就是求每个子树内深度等于 u u u的有多少个节点
开始我蠢了,在 d s u dsu dsu的过程计数的是相对深度
但是重儿子合并到父亲节点的时候这个相对深度就变化了啊!!还找了好久 b u g . . . bug... bug...
#include <bits/stdc++.h>
using namespace std;
const int maxn = 4e5+10;
struct edge{
int to,nxt;
}d[maxn]; int head[maxn],cnt=1;
void add(int u,int v){
d[++cnt] = (edge){v,head[u]},head[u] = cnt;
}
struct query{
int to,nxt,id,ans;
bool operator < (const query&tmp ) const{
return id<tmp.id;
}
}d1[maxn]; int head1[maxn],cnt1=1;
void addquery(int u,int k,int id){
d1[++cnt1] = (query){k,head1[u],id,0},head1[u] = cnt1;
}
int siz[maxn],son[maxn],dep[maxn],fa[maxn][22],n,isok[maxn],deep[maxn];
void dfs(int u,int ffa)
{
siz[u] = 1; fa[u][0] = ffa; deep[u] = deep[ffa]+1;
for(int i=1;i<=18;i++)
fa[u][i] = fa[ fa[u][i-1] ][i-1];
for(int i=head[u];i;i=d[i].nxt )
{
int v = d[i].to;
if( v==ffa ) continue;
dfs(v,u);
siz[u] += siz[v];
if( siz[v]>siz[son[u]] ) son[u] = v;
}
}
int SON;
void insert(int u,int ffa,int val)
{
dep[ deep[u] ]+=val;
for(int i=head[u];i;i=d[i].nxt )
{
int v = d[i].to;
if( v==ffa || v==SON ) continue;
insert( v,u,val);
}
}
void dsu(int u,int ffa,int type)
{
for(int i=head[u];i;i=d[i].nxt )
{
int v = d[i].to;
if( v==son[u]||v==ffa ) continue;
dsu(v,u,0);//不保存
}
if( son[u] ) dsu(son[u],u,1),SON = son[u];
insert( u,ffa,1 );
SON = 0;
for(int i=head1[u];i;i=d1[i].nxt )
{
d1[i].ans = dep[ deep[d1[i].to] ];
}
if( !type ) insert( u,ffa,-1 );
}
int kth(int x,int k)
{
for(int i=18;i>=0;i--)
if( k>=(1<<i) ) k-=(1<<i), x = fa[x][i];
return x;
}
int main()
{
cin >> n;
for(int i=1;i<=n;i++)
{
scanf("%d",&isok[i]);
if( isok[i] )
add( isok[i],i ); add( i,isok[i] );
}
for(int i=1;i<=n;i++)
if( !isok[i] ) dfs(i,0);
int q; scanf("%d",&q);
for(int i=1;i<=q;i++)
{
int u,k; scanf("%d%d",&u,&k);
addquery( kth(u,k),u,i );
}
for(int i=1;i<=n;i++)
if( !isok[i] ) dsu(i,0,0);
sort( d1+2,d1+2+q );
for(int i=2;i<=q+1;i++)
printf("%d ",max( 0,d1[i].ans-1) );
}