【JZOJ6046】交通

Description

给出一个 n n n个点以 1 1 1为根的树,求出对于任意的 i i i,把 i i i及其相连的边删掉后,使一个点改变它的父亲后形成的连通块的最大值最小,对于每个 i i i输出连通块的大小。
n ≤ 1 0 5 n\leq10^5 n105

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 a1a2ak
k = 1 k=1 k=1或者 a k = a k − 1 a_k=a_{k-1} ak=ak1时,答案即是 a k a_k ak
那么剩余的情况一定是将连通块 k k k中选一个点,更改它的父亲为连通块 1 1 1中的一个点,然后选择的这个点的子树大小要尽量接近 a k − a 1 2 \dfrac{a_k-a_1}{2} 2aka1(相当于使这两个连通块大小尽量接近),然后对次大值取 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]);
}

posted @ 2019-03-14 22:03  sadstone  阅读(36)  评论(0编辑  收藏  举报