Codeforces 208E. Blood Cousins
题目大意:
小C喜欢研究族谱,这一天小C拿到了一整张族谱。
小C先要定义一下k-祖先。
x的1-祖先指的是x的父亲
x的k-祖先指的是x的(k-1)-祖先的父亲
小C接下来要定义k-兄弟
x的k-兄弟指的是与x的k-祖先相同的人
如果不存在k-祖先那么x没有k-兄弟
小C想问问你,x到底有多少k-兄弟?小C打算问Q次这样的问题。
数据范围:
$n<=10^5,Q<=10^5$
$dsu\ on\ tree$ 基础题,当然也有显然的在线做法
对于每个询问 $(x,k)$,不妨转换为 $(u,v)$ ,表示求 $u$ 的子树中,与 $v$ 深度相同的节点数
考虑怎么离线搞,直接 $dsu\ on\ tree$ 维护一个统计各个深度节点数的数组 $cnt$ 即可
每次 $dfs$ 时最后 $dfs$ 重儿子,从而保留重儿子的 $cnt$ ,这样与每个节点 $u$ 有关的询问只要暴力枚举轻儿子子树即可
复杂度就是启发式合并的 $nlog_n$
具体看代码,注意数据是森林
#include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #include<cstring> #include<set> using namespace std; typedef long long ll; inline int read() { int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } const int N=2e6+7; int fir[N],from[N<<1],to[N<<1],cntt; inline void add(int a,int b) { from[++cntt]=fir[a]; fir[a]=cntt; to[cntt]=b; } int f[N][21],dep[N],son[N],sz[N];//f是倍增数组,dep是节点深度,son是重儿子,sz是子树大小 void dfs1(int x)//预处理上面四个数组 { dep[x]=dep[f[x][0]]+1; sz[x]=1; int mx=0; for(int i=1;i<=20;i++) f[x][i]=f[f[x][i-1]][i-1]; for(int i=fir[x];i;i=from[i]) { int &v=to[i]; if(v==f[x][0]) continue; f[v][0]=x; dfs1(v); if(sz[v]>sz[son[x]]) son[x]=v; sz[x]+=sz[v]; } } struct dat{ int x,y,id; inline bool operator < (const dat &tmp) const { return x<tmp.x; } }d[N];//存询问 int n,Q; int cnt[N],ans[N],id[N],dfs_clock;//id是dfs序为i的节点编号 void dfs2(int x,bool flag)//flag判断是否保留cnt { id[++dfs_clock]=x; int L=dfs_clock;//轻儿子子树dfs序的左区间 if(!son[x]) { if(flag) cnt[dep[x]]++; return; } for(int i=fir[x];i;i=from[i]) { int &v=to[i]; if(v==f[x][0]||v==son[x]) continue; dfs2(v,0);//走轻儿子 } int R=dfs_clock,t=lower_bound(d+1,d+Q+1,(dat){x,0,0})-d;//R是轻儿子子树右区间,t是第一个u为x的询问的位置 dfs2(son[x],1);//最后走重儿子并保留cnt for(int i=L;i<=R;i++) cnt[ dep[id[i]] ] ++;//枚举轻儿子子树,更新cnt for(int i=t;d[i].x==x;i++) { ans[d[i].id]=cnt[dep[d[i].y]]-1; }//更新ans if(!flag) for(int i=L;i<=dfs_clock;i++) cnt[ dep[id[i]] ]--;//清空cnt } int rt[N],tot;//注意是森林,要存每个数的根 int main() { n=read(); int a,b; for(int i=1;i<=n;i++) { a=read(); add(a,i); if(!a) rt[++tot]=i; } for(int i=1;i<=tot;i++) dfs1(rt[i]); Q=read(); for(int i=1;i<=Q;i++) { a=read(),b=read(); int t=a; for(int j=20;j>=0;j--) if(b&(1<<j)) t=f[t][j]; d[i].x=t,d[i].y=a,d[i].id=i; } sort(d+1,d+Q+1); for(int i=1;i<=tot;i++) dfs2(rt[i],0); for(int i=1;i<=Q;i++) printf("%d ",ans[i]); return 0; }