P5811 [IOI2019] 景点划分 题解

不妨令 abca\leq b\leq c。如果题目输入的不满足则可以交换。我们只考虑构造两个大小分别为 aabb 的连通块。其他点全都在 CC 中。考虑如果选的大小不是 aabb 连通块,比如选的是 aacc,那么可以把 CC 中某些点删掉并不影响连通性,使得这个连通块大小为 bb

先考虑原图是一棵树的时候怎么办?不妨枚举每一条边,设两边子树大小分别为 x,y(xy)x,y(x \leq y)。若 xax \geq ayby \geq b,则显然可以在两侧选连通块。

然而注意到这个事情不优美,不容易拓展到图上,我们考虑是否需要对每条边都这样考虑。事实上,我们找到树的重心,考虑将树视为以重心为根的树。考虑重心的某个儿子,如果子树大小 a\geq a,则一定有解。否则无解。

证明:

先证明充分性,即如果存在子树大小 a\geq a,必然有解。我们只需要在这个子树中选 aa 个点即可。考虑还剩多少点。除了这个子树,还剩 nszun-sz_u 个点。由于我选的是重心,szun2sz_u \leq \dfrac{n}{2},故 nszun2n-sz_u \geq \dfrac{n}{2}。由于 a+b+c=na+b+c=nabca\leq b\leq c。必然有 bn2b \leq \dfrac{n}{2}。所以通过重心和其他子树一定能构造出这样的连通块。

接着证明如果不存在这样的子树则无解。如果不存在这样的子树,那么对于 aa 这个连通块而言,必然是经过重心以及大于等于 22 个子树所形成的。那么对于 bb 而言只能选重心的某个子树。但是如果能选出这样的子树,由于 bab \geq a,必然也能选出对于 aa 也成立的子树。故矛盾。

于是树上问题就处理完了。

考虑图上问题。我们想让图和树有联系,必然是求出图的一棵 DFS 生成树,找出重心。如果重心有一个儿子子树大小 a\geq a,就直接做完了。但是如果不存在这样的子树,我们注意到图上还有一些非树边,这些边可能会影响答案。此时不一定是无解了。

考虑 DFS 生成树的根为 11,选出的重心为 uu。不妨令 u1u \neq 1。考虑到这是 DFS 生成树,非树边必然是返祖边,于是集合 AA 必然是 uu 子树外的所有点和 uu 的某些儿子的子树中的若干个点。不包含 uu。我们枚举 uu 所有儿子,判断这个子树是否有返祖边能回去,如果有就累加子树大小。如果子树大小 a\geq a 就有解,否则无解。

证明:

考虑最终累加的子树大小必然是 <2a<2a 的,因为最后一步累加的 <a<a,之前的也 <a<a。此时图上至少还有 n2an-2a 个点。同时又有 a+b+c=na+b+c=n,故图上还有 a+b+c2a=b+caa+b+c-2a=b+c-a 个点。由于 cac \geq a,必然有 b+cabb+c-a \geq b,于是我们必然可以选出这个大小为 bb 的集合。

于是整个题就做完了。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string>
#include <cassert>
#include <queue>
#include <vector>
using namespace std;

const int N = 2e5 + 5;

int n, m;
vector<int> G[N];
int A, B, C, rA, rB, rC;

int sz[N], fa[N];
int ans[N];
bool vis[N];
vector<int> NG[N];
int wc;
pair<int, int> a[5];
int id[N], idx, ra[N];

void dfs(int u, int f)
{
	id[u] = ++idx;
	ra[idx] = u;
	fa[u] = f;
	vis[u] = 1;
	int maxn = 0;
	sz[u] = 1;
	for (auto& j : G[u])
	{
		if (vis[j]) continue;
		NG[u].emplace_back(j);
		NG[j].emplace_back(u);
		dfs(j, u);
		sz[u] += sz[j];
		maxn = max(maxn, sz[j]);
	}
	maxn = max(maxn, n - sz[u]);
	if (maxn <= (n / 2)) wc = u;
}

int cnt = 0;

void Get_A(int u, int fa)
{
	ans[u] = a[1].second;
	cnt++;
	if (cnt == A) return;
	for (auto& j : NG[u])
	{
		if (j == fa) continue;
		Get_A(j, u);
		if (cnt == A) return;
	}
}

void Get_B(int u, int fa)
{
	if (ans[u]) return;
	ans[u] = a[2].second;
	cnt++;
	if (cnt == B) return;
	for (auto& j : NG[u])
	{
		if (j != fa)
		{
			Get_B(j, u);
			if (cnt == B) return;
		}
	}
}

class Union_Find
{
public:
	int fa[N];
	void Init()
	{
		for (int i = 0; i < N; i++) fa[i] = i;
	}
	int find(int u)
	{
		return (fa[u] == u ? u : fa[u] = find(fa[u]));
	}
	void merge(int u, int v)
	{
		fa[find(u)] = find(v);
	}
}uf;

bool flag[N];
int sflag[N];

int main()
{
	ios::sync_with_stdio(0), cin.tie(0);
	cin >> n >> m >> A >> B >> C;
	rA = A, rB = B, rC = C;
	a[1] = make_pair(A, 1);
	a[2] = make_pair(B, 2);
	a[3] = make_pair(C, 3);
	sort(a + 1, a + 4);
	A = a[1].first, B = a[2].first, C = a[3].first;
	for (int i = 1; i <= m; i++)
	{
		int u, v;
		cin >> u >> v;
		u++, v++;
		G[u].emplace_back(v);
		G[v].emplace_back(u);
	}
	dfs(1, 0);
	for (auto& j : NG[wc])
	{
		int szs = 0;
		if (j == fa[wc])
		{
			szs = n - sz[wc];
		}
		else szs = sz[j];
		if (szs >= A)
		{
			Get_A(j, wc);
			cnt = 0;
			Get_B(wc, 0);
			for (int i = 1; i <= n; i++)
			{
				if (!ans[i]) ans[i] = a[3].second;
			}
			for (int i = 1; i <= n; i++) cout << ans[i] << " ";
			cout << "\n";
			return 0;
		}
	}
	int faf = fa[wc];
	do
	{
		flag[faf] = 1;
		faf = fa[faf];
	} while (faf);
	int szs = n - sz[wc];
	vector<int> v;
	for (auto& j : NG[wc])
	{
		if (j == fa[wc]) continue;
		bool fg = 0;
		for (int k = id[j]; k < id[j] + sz[j]; k++)
		{
			for (auto& p : G[ra[k]])
			{
				if (flag[p]) fg = 1;
			}
		}
		if (!fg) continue;
		szs += sz[j];
		v.emplace_back(j);
		if (szs >= A)
		{
			Get_A(fa[wc], wc);
			for (auto& k : v)
			{
				Get_A(k, wc);
			}
			cnt = 0;
			Get_B(wc, 0);
			for (int i = 1; i <= n; i++)
			{
				if (!ans[i]) ans[i] = a[3].second;
			}
			for (int i = 1; i <= n; i++) cout << ans[i] << " ";
			cout << "\n";
			return 0;
		}
	}
	for (int i = 1; i <= n; i++) cout << "0 ";
	cout << "\n";
	return 0;
}
posted @   HappyBobb  阅读(10)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示