CF208E Blood Cousins

洛咕

题意:给你一片森林,每次询问一个点与多少个点拥有共同的\(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\n",ans[i]);
    return 0;
}

posted on 2019-10-13 16:04  PPXppx  阅读(102)  评论(0编辑  收藏  举报