【JZOJ6046】交通
Description
给出一个
n
n
n个点以
1
1
1为根的树,求出对于任意的
i
i
i,把
i
i
i及其相连的边删掉后,使一个点改变它的父亲后形成的连通块的最大值最小,对于每个
i
i
i输出连通块的大小。
n
≤
1
0
5
n\leq10^5
n≤105
Solution
把
i
i
i删掉后,设会形成
k
k
k个连通块,大小为
a
1
,
a
2
,
⋯
 
,
a
k
a_1,a_2,\cdots,a_k
a1,a2,⋯,ak,且
a
1
≤
a
2
≤
⋯
≤
a
k
a_1\leq a_2\leq \cdots \leq a_k
a1≤a2≤⋯≤ak。
当
k
=
1
k=1
k=1或者
a
k
=
a
k
−
1
a_k=a_{k-1}
ak=ak−1时,答案即是
a
k
a_k
ak。
那么剩余的情况一定是将连通块
k
k
k中选一个点,更改它的父亲为连通块
1
1
1中的一个点,然后选择的这个点的子树大小要尽量接近
a
k
−
a
1
2
\dfrac{a_k-a_1}{2}
2ak−a1(相当于使这两个连通块大小尽量接近),然后对次大值取
m
a
x
max
max。
我们用线段树合并维护一个点往下出现的所有子树大小的集合,当连通块
k
k
k为
i
i
i的儿子时,直接查询。
考虑如果是
i
i
i父亲那边的连通块,我们发现只有
i
i
i到根这条路径的点对应的子树大小集合会减去子树
i
i
i的大小,剩余的都是不变的。
那么一开始处理出全局的线段树合并,往下遍历
i
i
i的时候把子树
i
i
i大小在这棵线段树上减去一次,查询时就相当于在全局的线段树减去
i
i
i往下的线段树查询。
还剩
i
i
i到根的子树需要查询,我们发现它们是自上往下单调递增的,于是开个栈记录一下二分即可。
Code
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define fo(i,j,k) for(int i=j;i<=k;++i)
#define fd(i,j,k) for(int i=j;i>=k;--i)
#define rep(i,x) for(int i=ls[x];i;i=nx[i])
using namespace std;
const int N=1e5+10,inf=1e9;
int to[N],nx[N],ls[N],num=0;
void link(int u,int v){
to[++num]=v,nx[num]=ls[u],ls[u]=num;
}
int fa[N];
struct node{
int v,l,r,s;
};
int n,mx;
struct tree{
node tr[N*55];
int rt[N],tot=0;
void add(int x,int t,int &v,int l=1,int r=mx){
if(!v) v=++tot;
tr[v].s+=t;
if(l==r) return;
int mid=(l+r)>>1;
x<=mid?add(x,t,tr[v].l,l,mid):add(x,t,tr[v].r,mid+1,r);
}
void merge(int &v,int v1,int l=1,int r=mx){
if(!v) return void(v=v1);
tr[v].s+=tr[v1].s;
if(!v1 || l==r) return;
int mid=(l+r)>>1;
merge(tr[v].l,tr[v1].l,l,mid);
merge(tr[v].r,tr[v1].r,mid+1,r);
}
void mrg(int x,int y) {merge(rt[x],rt[y]);}
void ins(int x,int t,int rr) {add(x,t,rt[rr]);}
}F,G;
int ql(int x,int v,int v1,int l=1,int r=mx){
if(!(F.tr[v].s-G.tr[v1].s)) return -1;
if(l==r) return l;
int mid=(l+r)>>1,p=-1;
if(x>mid) p=ql(x,F.tr[v].r,G.tr[v1].r,mid+1,r);
if(p==-1 && x>=l) p=ql(x,F.tr[v].l,G.tr[v1].l,l,mid);
return p;
}
int qr(int x,int v,int v1,int l=1,int r=mx){
if(!(F.tr[v].s-G.tr[v1].s)) return -1;
if(l==r) return l;
int mid=(l+r)>>1,p=-1;
if(x<=mid) p=qr(x,F.tr[v].l,G.tr[v1].l,l,mid);
if(p==-1 && x<=r) p=qr(x,F.tr[v].r,G.tr[v1].r,mid+1,r);
return p;
}
int sz[N];
void pre(int x){
sz[x]=1;
rep(i,x){
int v=to[i];
pre(v),sz[x]+=sz[v];
F.mrg(x,v);
}
F.ins(2*sz[x],1,x);
}
int an[N];
void up(int &x,int &y,int &z,int w){
if(x<w) y=x,x=w;
else if(y<w) y=w;
if(w && z>w) z=w;
}
int val(int p,int q){
return 2*(sz[p]-sz[q]);
}
int st[N],top=0;
int ef(int x,int t){
int l,r;
for(l=1,r=top;l+1<r;){
int mid=(l+r)>>1;
val(st[mid],x)>=t?l=mid:r=mid;
}
if(val(st[r],x)>=t) l=r;
if(l<top && val(st[l],x)-t>t-val(st[l+1],x)) l=l+1;
return sz[st[l]]-sz[x];
}
void dfs(int x){
F.ins(2*sz[x],-1,1),st[++top]=x;
int pos=0;
rep(i,x){
int v=to[i];
dfs(v);
if(sz[pos]<sz[v]) pos=v;
}
st[top--]=0;
int mx1=0,mx2=0,mn=n;
rep(i,x) up(mx1,mx2,mn,sz[to[i]]);
up(mx1,mx2,mn,n-sz[x]);
if(mx1==mx2 || !mx2) an[x]=mx1;
else{
int t=mx1-mn,tmp=inf;
if(mx1==n-sz[x]){
if(top){
int t1=ef(x,t);
tmp=min(tmp,max(mn+t1,mx1-t1));
}
rep(i,x) G.mrg(x,to[i]);
int p=ql(t,F.rt[1],G.rt[x]),q=qr(t,F.rt[1],G.rt[x]);
if(q>0 && t-p>q-t) p=q;
if(p>0) tmp=min(tmp,max(mn+p/2,mx1-p/2));
}
else{
int p=ql(t,0,G.rt[pos]),q=qr(t,0,G.rt[pos]);
if(q>0 && t-p>q-t) p=q;
if(p>0) tmp=max(mn+p/2,mx1-p/2);
rep(i,x) G.mrg(x,to[i]);
}
an[x]=max(tmp,mx2);
}
F.ins(2*sz[x],1,1),G.ins(2*sz[x],1,x);
}
int main()
{
scanf("%d",&n),mx=n*2;
fo(i,2,n) scanf("%d",&fa[i]),link(fa[i],i);
pre(1);
dfs(1);
fo(i,1,n) printf("%d\n",an[i]);
}