题目描述:
`A` 国一年一度的盛会——国际健美操大赛就要开始了。
`A` 国共有 $n$ 座城市,构成一棵以 $1$ 号城市为根的有根树,除了 $1$ 号城市以外,每个城市都有唯一的一个依附城市。健美操大会将在 `A` 国 $n$ 个城市中的某一个举行,定义在城市 $x$ 举行盛会的交通不便程度为将城市 $x$ 删去后最大联通块的大小。
为了提高交通运输能力,`A` 国政府可以进行一次如下操作:在删去 $x$ 号城市后,可以更改某个城市的依附城市(不能对 $1$ 号城市和依附城市为 $x$ 的城市进行该操作)。现在 `A` 国的国王想要知道对于每个城市而言,在这个城市举行盛会的交通不便程度最小可能是多少。
算法标签:线段树
思路:
最优的移动策略无疑是把某一个子树从最大的联通块移动到最小的联通块。
所以得出我们要维护分割后每个联通块的内所有节点的子树大小,还有当最大联通块是自己的父亲部分时,需要和我同链的节点子树大小以及和我不同链且不再我的子树内的节点子树大小。
对于自己子树的节点大小可以在 $dfs$ 的过程中线段树合并达成,对于与我同链的可以提前用可持久化线段树维护,对于不同链也不在子树的,可以用全集-与我同链-在我的子树内的。
接下来对于 $(sz[mx]-sz[mn])/2$ 二分寻找最接近它的数。
以下代码:
#include<bits/stdc++.h> #define il inline #define _(d) while(d(isdigit(ch=getchar()))) using namespace std; const int N=1e5+5,inf=1e9; int tot,q[N],top,n; int head[N],ne[N],to[N],cnt,rt[N],ret[N],sz[N],rt1[N]; struct node{ int l,r,num; }t[N*80]; il int read(){ int x,f=1;char ch; _(!)ch=='-'?f=-1:f;x=ch^48; _()x=(x<<1)+(x<<3)+(ch^48); return f*x; } bool cmp(int t1,int t2){ return sz[t1]<sz[t2]; } il void ins(int x,int y){ ne[++cnt]=head[x]; head[x]=cnt;to[cnt]=y; } il void insert(int &x,int l,int r,int pos){ if(!x)x=++tot;t[x].num++; if(l==r)return;int mid=(l+r)>>1; if(pos<=mid)insert(t[x].l,l,mid,pos); else insert(t[x].r,mid+1,r,pos); } il void Ins(int &x,int y,int l,int r,int pos){ t[x=++tot]=t[y];t[x].num++; if(l==r)return;int mid=(l+r)>>1; if(pos<=mid)Ins(t[x].l,t[y].l,l,mid,pos); else Ins(t[x].r,t[y].r,mid+1,r,pos); } il void dfs(int x){ sz[x]=1; for(int i=head[x];i;i=ne[i]){ dfs(to[i]),sz[x]+=sz[to[i]]; } } il void dfs1(int x){ for(int i=head[x];i;i=ne[i]){ Ins(rt1[to[i]],rt1[x],1,n,sz[x]); dfs1(to[i]); } insert(rt[0],1,n,sz[x]); } il void merge(int &x,int y,int l,int r){ if(!x||!y){x=x+y;return;} t[x].num+=t[y].num;if(l==r)return; int mid=(l+r)>>1; merge(t[x].l,t[y].l,l,mid); merge(t[x].r,t[y].r,mid+1,r); } il bool query(int x,int l,int r,int ql,int qr){ if(!t[x].num)return 0; if(ql<=l&&r<=qr)return 1; int mid=(l+r)>>1; if(ql<=mid&&query(t[x].l,l,mid,ql,qr))return 1; if(mid<qr&&query(t[x].r,mid+1,r,ql,qr))return 1; return 0; } il int bigger(int x,int v){ if(!query(x,1,n,v,n))return inf; int l=v,r=n,res=inf; while(l<=r){ int mid=(l+r)>>1; if(query(x,1,n,v,mid))res=mid,r=mid-1; else l=mid+1; } return res; } il int smler(int x,int v){ if(!query(x,1,n,1,v))return inf; int l=1,r=v,res=inf; while(l<=r){ int mid=(l+r)>>1; if(query(x,1,n,mid,v))l=mid+1,res=mid; else r=mid-1; } return res; } il bool Q(int x,int y,int z,int l,int r,int ql,int qr){ if((t[z].num-t[x].num-t[y].num)==0)return 0; if(ql<=l&&r<=qr)return 1; int mid=(l+r)>>1; if(ql<=mid&&Q(t[x].l,t[y].l,t[z].l,l,mid,ql,qr))return 1; if(mid<qr&&Q(t[x].r,t[y].r,t[z].r,mid+1,r,ql,qr))return 1; return 0; } il int B(int x,int y,int z,int v){ if(!Q(x,y,z,1,n,v,n))return inf; int l=v,r=n,res=inf; while(l<=r){ int mid=(l+r)>>1; if(Q(x,y,z,1,n,v,mid))res=mid,r=mid-1; else l=mid+1; } return res; } il int S(int x,int y,int z,int v){ if(!Q(x,y,z,1,n,1,v))return inf; int l=1,r=v,res=inf; while(l<=r){ int mid=(l+r)>>1; if(Q(x,y,z,1,n,mid,v))l=mid+1,res=mid; else r=mid-1; } return res; } il void dfs2(int x){ for(int i=head[x];i;i=ne[i])dfs2(to[i]); if(!head[x]){insert(rt[x],1,n,sz[x]);ret[x]=n-1;return;} top=0; for(int i=head[x];i;i=ne[i])q[++top]=to[i]; if(x!=1)q[++top]=0,sz[0]=n-sz[x]; sort(q+1,q+1+top,cmp); int res=sz[q[top]],y; if(top==1){ret[x]=sz[q[1]];return;} if(q[top]!=0){ int st; st=(sz[q[top]]-sz[q[1]]+1)/2; y=bigger(rt[q[top]],st); res=min(max(sz[q[1]]+y,sz[q[top]]-y),res); y=smler(rt[q[top]],st); res=min(max(sz[q[1]]+y,sz[q[top]]-y),res); } insert(rt[x],1,n,sz[x]); for(int i=head[x];i;i=ne[i])merge(rt[x],rt[to[i]],1,n); if(q[top]==0){ int st; st=(sz[q[top]]-sz[q[1]]+1)/2; y=B(rt[x],rt1[x],rt[0],st); res=min(max(sz[q[1]]+y,sz[q[top]]-y),res); y=S(rt[x],rt1[x],rt[0],st); res=min(max(sz[q[1]]+y,sz[q[top]]-y),res); st=(sz[q[top]]-sz[q[1]]+1)/2+sz[x]; y=B(0,0,rt1[x],st); res=min(max(sz[q[1]]+y-sz[x],sz[q[top]]-y+sz[x]),res); y=S(0,0,rt1[x],st); res=min(max(sz[q[1]]+y-sz[x],sz[q[top]]-y+sz[x]),res); } if(top>2)res=max(res,sz[q[top-1]]); ret[x]=res; } int main() { n=read(); for(int i=2;i<=n;i++)ins(read(),i); dfs(1); rt[0]=++tot;dfs1(1); dfs2(1); for(int i=1;i<=n;i++)printf("%d ",ret[i]); return 0; }