#長鏈剖分#CF208E Blood Cousins
阿新 • • 發佈:2021-07-07
長鏈剖分
題目
給你一片森林,每次詢問一個點與多少個點擁有共同的 \(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; }