codeforces768G.The Winds of Winter

题面


题意:对于每个点单独考虑,删掉它,你可以把一个节点的父亲改成另一个节点(只能做一次),使森林中最大的树size最小。


这题的思路比较简单,对于森林中的所有树,显然应该把最大的那颗树的一部分接到最小的上,然后对次大树,最大树·改,最小树·改取max

然后如何找最优的呢?
显然减(mx-cx)/2是最优的,然后就开权值线段树,查询1~(mx-cx)/2最大值
查询(mx-cx)/2~n最小值
对于最大树是儿子,就按照树上dsu合并;
如果是父亲,分成两部分,
一部分是到根路径上的点,查询时l,r加上siz[u],因为他们的size要减siz[u]
另一部分是全局的减去当前子树的,不包含到根路径上的点


#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=2e5+7,inf=1e8;
struct edge {
	int t,nxt;
}e[N];
struct node {
	int s,tag;
};
int n,cnt,rt;
int head[N],siz[N],Mx[N],Cx[N],Mn[N],Mson[N],Bigs[N],ans[N],vis[N];
void add(int u,int t) {
	e[++cnt].t=t;e[cnt].nxt=head[u];head[u]=cnt;
}
struct tree {
	#define ls ts<<1
	#define rs ts<<1|1
	node tr[N<<1];
	void pushdown(int ts) {
		if (!tr[ts].tag) return;
		tr[ls].s=0;tr[rs].s=0;
		tr[ls].tag=1;tr[rs].tag=1;
		tr[ts].tag=0;
	}
	inline void add(int ts,int l,int r,int pos,int x) {
		pushdown(ts);
		tr[ts].s+=x;
		if (l==r) return;
		int mid=(l+r)>>1;
		if (pos<=mid) add(ls,l,mid,pos,x);
		else add(rs,mid+1,r,pos,x);
	}
	inline void clear() {
		tr[1].s=0;tr[1].tag=1;
	}
	inline int query2(int ts,int l,int r,int L,int R) {
		if (L>R || !tr[ts].s) return 0;
		if (l==r) return l;
		pushdown(ts);
		int mid=(l+r)>>1;
		if (R<=mid) return query2(ls,l,mid,L,R);
		else if (L>mid) return query2(rs,mid+1,r,L,R);
		else {
			int v=query2(rs,mid+1,r,mid+1,R);
			if (v) return v;
			else return query2(ls,l,mid,L,mid);//1~(mx-cx)/2最大值
		}
	}
	inline int query3(int ts,int l,int r,int L,int R) {
		if (L>R || !tr[ts].s) return inf;
		if (l==r) return l;
		pushdown(ts);
		int mid=(l+r)>>1;
		if (R<=mid) return query3(ls,l,mid,L,R);
		else if (L>mid) return query3(rs,mid+1,r,L,R);
		else {
			int v=query3(ls,l,mid,L,mid);
			if (v!=inf) return v;
			else return query3(rs,mid+1,r,mid+1,R);//(mx-cx)/2~n最小值
		}
	}
}T1,T2,T3;//T1总共的siz,T2所有祖先的siz,T3节点当前siz 
void chu(int &mx,int &cx,int &mn,int v,int &id,int t) {
	if (v>mx) cx=mx,mx=v,id=t;
	else if (v>cx) cx=v;
	if (v) mn=min(mn,v);
}//找最大次大最小 
void dfs1(int u) {
	siz[u]=1;
	Mson[u]=-1;
	for (int i=head[u];i;i=e[i].nxt) {
		int t=e[i].t;
		dfs1(t);
		siz[u]+=siz[t];
		chu(Mx[u],Cx[u],Mn[u],siz[t],Mson[u],t);
	}
	Bigs[u]=Mson[u];
	chu(Mx[u],Cx[u],Mn[u],n-siz[u],Mson[u],0);
	T1.add(1,1,n,siz[u],1);
}
void Add(int u) {
	T3.add(1,1,n,siz[u],1);
	for (int i=head[u];i;i=e[i].nxt) {
		int t=e[i].t;
		if (vis[t]) continue;
		Add(t);
	}
}
int query2(int ts,int l,int r,int L,int R) {
	if (L>R || T1.tr[ts].s-T3.tr[ts].s<=0) return 0;
	if (l==r) return l;
	T3.pushdown(ts);
	int mid=(l+r)>>1;
	if (R<=mid) return query2(ls,l,mid,L,R);
	else if (L>mid) return query2(rs,mid+1,r,L,R);
	else {
		int v=query2(rs,mid+1,r,mid+1,R);
		if (v) return v;
		else return query2(ls,l,mid,L,mid);
	}//父亲查找max
}
int query3(int ts,int l,int r,int L,int R) {
	if (L>R || T1.tr[ts].s-T3.tr[ts].s<=0) return inf;
	if (l==r) return l;
	T3.pushdown(ts);
	int mid=(l+r)>>1;
	if (R<=mid) return query3(ls,l,mid,L,R);
	else if (L>mid) return query3(rs,mid+1,r,L,R);
	else {
		int v=query3(ls,l,mid,L,mid);
		if (v!=inf) return v;
		else return query3(rs,mid+1,r,mid+1,R);
	}//父亲查找min 
}
int chuu(int a,int b) {
	return (a+b)&1;
}
void dfs2(int u) {
	T1.add(1,1,n,siz[u],-1);
	T2.add(1,1,n,siz[u],1);
	for (int i=head[u];i;i=e[i].nxt) {
		int t=e[i].t;
		if (t==Bigs[u]) continue;
		dfs2(t);
		T3.clear();
	}
	if (Bigs[u]!=-1) dfs2(Bigs[u]),vis[Bigs[u]]=1;
	int v=0;
	if (Mson[u]==Bigs[u]) {
		v=T3.query2(1,1,n,1,(Mx[u]-Mn[u])/2+chuu(Mx[u],Mn[u]));
		ans[u]=max(max(Mx[u]-v,Mn[u]+v),Cx[u]);
		v=T3.query3(1,1,n,(Mx[u]-Mn[u])/2,n);
		ans[u]=min(ans[u],max(max(Mx[u]-v,Mn[u]+v),Cx[u]));
	}
	Add(u);
	if(Bigs[u]!= -1) vis[Bigs[u]] = 0;
	T1.add(1,1,n,siz[u],1);
	T2.add(1,1,n,siz[u],-1);
	if (Mson[u]!=Bigs[u]) {
		v=max(query2(1,1,n,1,(Mx[u]-Mn[u])/2+chuu(Mx[u],Mn[u])),T2.query2(1,1,n,1+siz[u],min((Mx[u]-Mn[u])/2+siz[u]+chuu(Mx[u],Mn[u]),n))-siz[u]);
		ans[u]=max(max(Mx[u]-v,Mn[u]+v),Cx[u]);
		v=min(query3(1,1,n,(Mx[u]-Mn[u])/2,n),T2.query3(1,1,n,(Mx[u]-Mn[u])/2+siz[u],n)-siz[u]);
		ans[u]=min(ans[u],max(max(Mx[u]-v,Mn[u]+v),Cx[u]));
	}
}
int main() {
	scanf("%d",&n);
	if (n==1) return puts("0"),0;
	memset(Mn,60,sizeof Mn);
	for (int i=1,u,v;i<=n;i++) {
		scanf("%d%d",&u,&v);
		if (u==0) rt=v;
		else add(u,v);
	}
	dfs1(rt);
	dfs2(rt);
	for (int i=1;i<=n;i++) printf("%d\n",ans[i]);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值