1. 程式人生 > 其它 >#長鏈剖分#CF208E Blood Cousins

#長鏈剖分#CF208E Blood Cousins

長鏈剖分

題目

給你一片森林,每次詢問一個點與多少個點擁有共同的 \(K\) 級祖先


分析

\(dp[x][d]\)表示以\(x\)為根節點時深度為\(d\)的個數,
那麼\(dp[x][d]=\sum\{dp[y][d-1]\}\)
這個考慮用長鏈剖分維護就可以做到\(O(n)\)


程式碼

#include <cstdio>
#include <cctype>
#define rr register 
using namespace std;
const int N=100011; struct node{int y,next;}e[N],E[N]; int puf[N]; int *dp[N],*st=puf;
int dep[N],dfn[N],sta[N],big[N],F[N],Top,d[N],ans[N],n,Q,et,rt[N],as[N],hs[N],Hs[N];
inline signed iut(){
	rr int ans=0; rr char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
	return ans;
}
inline void print(int ans){
	if (ans>9) print(ans/10);
	putchar(ans%10+48);
}
inline void add(int x,int y){
	if (!x) {rt[++rt[0]]=y; return;}
    e[++et]=(node){y,as[x]},as[x]=et;
}
inline void dfs1(int x,int fa){
	dep[x]=dep[fa]+1,sta[++Top]=x;
	for (rr int i=as[x];i;i=e[i].next)
	if (e[i].y!=fa){
		dfs1(e[i].y,x);
		if (d[e[i].y]>d[big[x]]) big[x]=e[i].y;
	}
	for (rr int i=hs[x];i;i=E[i].next)
	    if (Top>=E[i].y) F[i]=sta[Top-E[i].y];
	--Top,d[x]=d[big[x]]+1;
}
inline void dfs2(int x,int fa){
	dp[x][0]=1;
	if (!big[x]) return;
	dp[big[x]]=dp[x]+1,dfs2(big[x],x);
	for (rr int i=as[x];i;i=e[i].next)
 	if (e[i].y!=fa&&e[i].y!=big[x]){
		dp[e[i].y]=st,st+=d[e[i].y],dfs2(e[i].y,x);
		for (rr int j=1;j<=d[e[i].y];++j)
		    dp[x][j]+=dp[e[i].y][j-1];
	}
	for (rr int i=hs[x];i;i=E[i].next)
	    ans[i]=dp[x][E[i].y]-1;
}
signed main(){
	n=iut();
	for (rr int i=1;i<=n;++i) add(iut(),i);
	Q=iut();
	for (rr int i=1;i<=Q;++i){
		rr int x=iut(),d=iut();
		E[i]=(node){d,hs[x]},hs[x]=i;
	}
	for (rr int i=1;i<=rt[0];++i) dfs1(rt[i],0);
	for (rr int i=1;i<=n;++i) Hs[i]=hs[i],hs[i]=0;
	for (rr int i=1;i<=n;++i)
	for (rr int j=Hs[i],t;j;j=t){
		t=E[j].next;
		if (F[j]) E[j]=(node){E[j].y,hs[F[j]]},hs[F[j]]=j;
	}
	for (rr int i=1;i<=rt[0];++i)
		dp[rt[i]]=st,st+=d[rt[i]],dfs2(rt[i],0);
	for (rr int i=1;i<=Q;++i) print(ans[i]),putchar(32);
    return 0;
}