[IOI2019] 景点划分

对于树的情况,假设断掉 ( u , v ) (u,v) (u,v) ,考虑 u → S u\to S uS 的路径( S S S 表示树的重心)。令 v = S v=S v=S , 此时切掉的给 a a a 的部分比以前更大,且不超过 n / 2 n/2 n/2

请添加图片描述

所以我们找到树的重心,断掉重心到重儿子的边即可。

对于一般图,找到 D F S DFS DFS 树的重心 S S S

如果 max ⁡ ( T , S 1 , S 2 , . . . ) ≥ a \max(T,S1,S2, ... ) \geq a max(T,S1,S2,...)a ,记为 S i S_i Si ,那么 a ≤ S i ≤ n / 2 a \leq S_i \leq n/2 aSin/2, b ≤ n / 2 ≤ n − S i b \leq n/2 \leq n-S_i bn/2nSi

请添加图片描述

否则考虑无向图 D F S DFS DFS 树的性质,有一些 S i Si Si T T T 相连 。

请添加图片描述

不断剥离这样的子树,直到 T ≥ a T \geq a Ta

注意到 T ≤ 2 a T\leq2a T2a ,所以 n − T ≥ b n-T\geq b nTb

请添加图片描述

如果这样都不行的话,输出无解。(想一想为什么)。

#include<bits/stdc++.h>
using namespace std;
const int N=3e5+5;
int n,m,S,vis[N],siz[N],fa[N],son[N],dfn[N],rk[N],num,sa[N],dep[N];
vector<int> g[N],g2[N];
struct node{
	int val,id;
	bool operator <(const node &A)const {
		return val<A.val;
	}
}a[3];
inline int read() {
	int x=0,f=1; char c=getchar();
	while(c<'0'||c>'9') {
		if(c=='-') f=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9') {
		x=(x<<1)+(x<<3)+c-'0';
		c=getchar();
	}
	return x*f;
}
void dfs1(int u) {
	vis[u]=siz[u]=1,dfn[u]=++num,rk[num]=u;
	for(auto v:g[u]) {
		if(!vis[v]) g2[u].push_back(v),g2[v].push_back(u),fa[v]=u,dep[v]=dep[u]+1,dfs1(v),siz[u]+=siz[v],son[u]=(siz[v]>siz[son[u]])?v:son[u];
	}
	if(!S || max(n-siz[u],siz[son[u]]) < max(n-siz[S],siz[son[S]])) S = u;
} 
void dfs2(int u,int topf,int &x,int y) {
	if(x && !sa[u]) x--,sa[u]=y;
	else return;
	for(auto v:g2[u]) {
		if(v!=topf) dfs2(v,u,x,y);
	}
}
void ac() {
	for(int i=1;i<=n;i++) {
		if(!sa[i]) sa[i]=a[2].id;
		printf("%d ",sa[i]);
	}
	exit(0);
}
void wa() {
	for(int i=1;i<=n;i++) {
		printf("0 ");
	}
	exit(0);
}
int main() {
//	freopen("data.in","r",stdin);
	n=read(),m=read(),a[0].val=read(),a[1].val=read(),a[2].val=read();
	a[0].id=1,a[1].id=2,a[2].id=3;
	std::sort(a,a+3);
	for(int i=1;i<=m;i++) {
		int u=read()+1,v=read()+1;
		g[u].push_back(v);
		g[v].push_back(u);
	}
	dfs1(1);//printf("%d %d %d\n",S,son[S],siz[son[S]]);
	if(siz[son[S]] >= a[0].val) {
		dfs2(son[S],S,a[0].val,a[0].id);
		dfs2(S,son[S],a[1].val,a[1].id);
		ac();
	} 
	if(n-siz[S] >= a[0].val) {
		dfs2(fa[S],S,a[0].val,a[0].id);
		dfs2(S,fa[S],a[1].val,a[1].id);
		ac();
	}
	dfs2(fa[S],S,a[0].val,a[0].id);
	for(auto u:g2[S]) {
		if(u!=fa[S] && a[0].val) {
			int flg=0;
			for(int i=dfn[u];i<=dfn[u]+siz[u]-1;i++) {
				int v=rk[i];
				for(auto v2:g[v]) {
					if(dep[v2]<dep[S]) {
						flg=1;
					}
				}
			}
			if(flg) dfs2(u,S,a[0].val,a[0].id);
		}
	}
	if(a[0].val) {
		wa();
	}
	dfs2(S,fa[S],a[1].val,a[1].id);
	ac();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值