【CF768G】The Winds of Winter(启发式合并)(二分答案)

传送门


题解:

其实发现我们一定会在最大子树中选择一个子树接到最小的子树上。

假设我们二分这个答案为 x x x,所有子树中最大值为 m x mx mx,次大值为 s u b m x submx submx,最小值为 m n mn mn,则答案显然不可能小于 s u b m x submx submx,且我们选出来的子树大小 d e l t a delta delta 必须满足 m x − d e l t a ≤ x mx-delta\leq x mxdeltax m n + d e l t a ≤ x mn+delta\leq x mn+deltax,也就是 m x − x ≤ d e l t a ≤ x − m n mx-x \leq delta \leq x-mn mxxdeltaxmn。显然就是区间存在性问题。

用平衡树维护一下子树内有哪些可以选择的 s i z siz siz,注意当最大子树在父亲方向的时候,我们需要对全局维护平衡树,同时对当前点到根的路径专门维护一棵平衡树,因为在删掉 u u u的时候,这些点的 s i z siz siz全部都要减掉 s i z [ u ] siz[u] siz[u]

于是就能 O ( n log ⁡ 2 n ) O(n\log^2 n) O(nlog2n)做这道题了,空间复杂度 O ( n ) O(n) O(n)


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

namespace IO{
	inline char gc(){
		static cs int Rlen=1<<22|1;
		static char buf[Rlen],*p1,*p2;
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	
	template<typename T>
	inline T get(){
		char c;T num;
		while(!isdigit(c=gc()));num=c^48;
		while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
		return num;
	}
	inline int gi(){return get<int>();}
}
using namespace IO;

using std::cerr;
using std::cout;

cs int N=1e5+7;

namespace SBT{
	cs int N=::N<<2|1;
	int lc[N],rc[N];
	int val[N],c[N],siz[N],rsiz[N];
	int tot,dl[N],tp,q[N],qc[N],qn;
	
	inline int newnode(int v,int ct){
		int u=tp?dl[tp--]:++tot;
		lc[u]=rc[u]=0;val[u]=v,c[u]=siz[u]=ct,rsiz[u]=1;
		return u;
	}
	
	inline void pushup(int u){
		rsiz[u]=rsiz[lc[u]]+rsiz[rc[u]]+1;
		siz[u]=siz[lc[u]]+siz[rc[u]]+c[u];
	}
	
	inline void Zig(int &u){
		int v=lc[u];lc[u]=rc[v];rc[v]=u;
		pushup(u),pushup(v);u=v;
	}
	
	inline void Zag(int &u){
		int v=rc[u];rc[u]=lc[v];lc[v]=u;
		pushup(u),pushup(v);u=v;
	}
	
	cs double alpha=0.55;
	inline void ins(int &u,int v,int ct=1){
		if(!u){u=newnode(v,ct);return ;}
		siz[u]+=ct;
		if(val[u]==v)c[u]+=ct;
		else if(v<val[u]){
			ins(lc[u],v,ct);
			if(rsiz[lc[u]]>rsiz[u]*alpha)Zig(u);
		}
		else {
			ins(rc[u],v,ct);
			if(rsiz[rc[u]]>rsiz[u]*alpha)Zag(u);
		}
		pushup(u);
	}
	
	inline void del(int &u,int v){
		if(!u)return ;
		if(val[u]==v){
			if(c[u]>1)--c[u],--siz[u];
			else if(!lc[u]||!rc[u])dl[++tp]=u,u=lc[u]|rc[u];
			else if(rsiz[lc[u]]<rsiz[rc[u]])Zag(u),del(u,v);
			else Zig(u),del(u,v);
			if(u)pushup(u);
			return ;
		}
		if(v<val[u])del(lc[u],v);
		else del(rc[u],v);pushup(u);
	}
	
	inline int Rank(int u,int v){//count <=v 
		int ans=0;
		while(u){
			if(val[u]==v)return ans+siz[lc[u]]+c[u];
			if(val[u]<v)ans+=siz[lc[u]]+c[u],u=rc[u];
			else u=lc[u];
		}
		return ans;
	}
	inline int range(int rt,int l,int r){return Rank(rt,r)-Rank(rt,l-1);}
	
	void inorder_dfs(int u){
		if(lc[u])inorder_dfs(lc[u]);
		q[++qn]=val[u],qc[qn]=c[u];dl[++tp]=u;
		if(rc[u])inorder_dfs(rc[u]);
	}
	
	inline void Join(int &u,int &v){
		if(rsiz[u]<rsiz[v])std::swap(u,v);
		qn=0;inorder_dfs(v);v=0;
		for(int re i=1;i<=qn;++i)ins(u,q[i],qc[i]);
	}
}
using SBT::ins;
using SBT::del;
using SBT::range;
using SBT::Join;

int n;

int el[N],nxt[N<<1],to[N<<1],ect;
inline void adde(int u,int v){
	nxt[++ect]=el[u],el[u]=ect,to[ect]=v;
	nxt[++ect]=el[v],el[v]=ect,to[ect]=u;
}

int fa[N],siz[N];
void pre_dfs(int u){
	siz[u]=1;for(int re e=el[u],v=to[e];e;v=to[e=nxt[e]])
	if(v!=fa[u]){pre_dfs(v);siz[u]+=siz[v];}
}

int rt[N],rt_all,rt_path,root;
int ans[N];

inline bool check(int u,int mxson,int mn,int mx,int lim){
	int l=mx-lim,r=lim-mn;
	if(l>r)return false;
	if(mxson==fa[u]){
		int s1=range(rt_all,l,r);
		int s2=range(rt[u],l,r);
		int s3=range(rt_path,l+siz[u],r+siz[u]);
		return s1+s3>s2;
	}
	else {
		return range(rt[mxson],l,r)>0;
	}
}

void dfs_solve(int u){
	del(rt_all,siz[u]);
	if(u!=root)ins(rt_path,siz[u]);
	int mn=1e9,mx=0,submx=0,mxson=0;
	for(int re e=el[u],v=to[e];e;v=to[e=nxt[e]]){
		int siz_v=v==fa[u]?n-siz[u]:siz[v];
		if(v!=fa[u])dfs_solve(v);
		mn=std::min(siz_v,mn);
		if(siz_v>mx){
			submx=mx,mxson=v;
			mx=siz_v;
		}else if(siz_v>submx)submx=siz_v;
	}
	ins(rt_all,siz[u]);ins(rt[u],siz[u]);
	if(u!=root)del(rt_path,siz[u]);
	if(mxson==fa[u])for(int re e=el[u],v=to[e];e;v=to[e=nxt[e]])
	if(v!=fa[u])Join(rt[u],rt[v]);
	int l=submx==0?mx:submx,r=mx,mid;
	while(l<r)check(u,mxson,mn,mx,mid=l+r>>1)?r=mid:l=mid+1;ans[u]=l;
	if(mxson!=fa[u])for(int re e=el[u],v=to[e];e;v=to[e=nxt[e]])
	if(v!=fa[u])Join(rt[u],rt[v]);
}

#ifdef zxyoi
#undef zxyoi
#endif

signed main(){
#ifdef zxyoi
	freopen("winter.in","r",stdin);
#else
#ifndef ONLINE_JUDGE
	freopen("winter.in","r",stdin);freopen("winter.out","w",stdout);
#endif
#endif
	n=gi();
	for(int re i=1;i<=n;++i){
		int f=gi(),u=gi();
		if(!f)root=u;
		else adde(fa[u]=f,u);
	}pre_dfs(root);
	for(int re i=1;i<=n;++i)ins(rt_all,siz[i]);
	dfs_solve(root);
	for(int re i=1;i<=n;++i)cout<<ans[i]<<"\n";
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值