题面
题目大意:
给出一个n个点的森林,m次询问,每次求一个点的k级亲戚的数量
k级亲戚定义为,u≠v,u,v的k级祖先相同,则u,v为k级亲戚
题解
一定仔细读题md,是k级祖先,不是LCA为k级祖先
我的做法是先倍增求点u的k级祖先
然后统计k级祖先下深度为dep[u]点的个数
想来想去没想到好方法,然后就dfs序+莫队莽过去了
后来发现可以线段树合并,dsu on tree,每个点开一个线段树,
然后把询问挂在点上,遍历一遍,一个一个合并线段树,合并完之后做查询记录答案。
再后来发现dfs序+主席树可以在线。
代码(莫队):
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
inline int gi()
{
int num=0,flg=1;char c;
while((c=getchar())<'0'||c>'9')if(c=='-')flg=-1;
while(c>='0'&&c<='9'){num=num*10+c-48;c=getchar();}
return num*flg;
}
#define N 100005
#define LOG 17
#define D 310
int fir[N],to[2*N],nxt[2*N],cnt;
int fa[N][LOG+2];
int getk(int x,int k)
{
for(int i=LOG;i>=0;i--)
if(k&(1<<i)) x=fa[x][i];
return x;
}
void adde(int a,int b)
{
to[++cnt]=b;nxt[cnt]=fir[a];fir[a]=cnt;
}
int dfn[N],siz[N],dep[N],dcnt;
int num[N];
void dfs(int u,int d)
{
dfn[u]=++dcnt;
num[dcnt]=u;
siz[u]=1;dep[u]=d;
for(int v,p=fir[u];p;p=nxt[p]){
v=to[p];
if(!dfn[v]){
dfs(v,d+1);
siz[u]+=siz[v];
}
}
}
int qcnt;
struct node{
int l,r,k,flg,id;
bool operator < (const node &t)const{
return l/D<t.l/D || (l/D==t.l/D && ((((l/D)&1) && r<t.r) || ( !((l/D)&1) && r>t.r)));
}
}q[N];
int depcnt[N];
int ans[N];
int main()
{
int n,m,i,j,x,v,k;
n=gi();
for(i=1;i<=n;i++){
fa[i][0]=gi();
adde(fa[i][0],i);
}
for(j=1;j<=LOG;j++)
for(i=1;i<=n;i++)
fa[i][j]=fa[fa[i][j-1]][j-1];
for(i=1;i<=n;i++)
if(!fa[i][0])dfs(i,0);
m=gi();
for(i=1;i<=m;i++){
v=gi();k=gi();
x=getk(v,k);
if(x!=0){
q[++qcnt].l=dfn[x];
q[qcnt].r=dfn[x]+siz[x]-1;
q[qcnt].k=dep[v];
q[qcnt].flg=1;
q[qcnt].id=i;
}
}
sort(q+1,q+qcnt+1);
int l=1,r=0;
for(i=1;i<=qcnt;i++){
while(r<q[i].r){
r++;
depcnt[dep[num[r]]]++;
}
while(l>q[i].l){
l--;
depcnt[dep[num[l]]]++;
}
while(l<q[i].l){
depcnt[dep[num[l]]]--;
l++;
}
while(r>q[i].r){
depcnt[dep[num[r]]]--;
r--;
}
ans[q[i].id]+=depcnt[q[i].k]*q[i].flg;
}
for(i=1;i<=m;i++){
if(ans[i])
printf("%d ",ans[i]-1);
else
printf("0 ");;
}
}