Loading

「清新题精讲」Gym100198H - Royal Federation

H - Royal Federation

\(\mathsf{\color{Thistle}Statement}\)

给定一棵 \(n\) 个点的树,将其划分为 \(m\) 个集合(\(m\) 可以为任意正整数),对于每个集合,顷定其特殊点,使得该点可以到达属于该集合内的所有点只经过集合内的点(注意特殊点可以不在集合内),其中集合大小要求在 \(B\sim 3B\) 之间。

\(\mathsf{\color{Thistle}Solution}\)

通过 DFS 由底向上合并,对于点 \(u\),考虑 \(u\) 的所 \(u\) 的儿子 \(v_i\),对于 \(\forall i,j\) 使得 \(v_i< B,v_j<B\),合并 \(i,j\)。若合并后还是 \(<B\),则在与其余儿子合并。

如果按照上述合并方式,最终剩余 \(1\) 个儿子 \(<B\),但无法合并,则将该儿子与 \(u\) 合并(目的是当统计 \(u\) 的父亲的时候,使得该儿子所在集合可以进一步合并)

注意:当处理完整棵树之后,有可能 \(1\) 点所在的集合 \(<B\),如若真如此,则将 \(1\) 点所在集合与下面的相邻的集合合并即可,可以证明合并完之后仍在 \(B\sim 3B\) 之间。

Proof 注意到与 1 点相邻的集合是由两个小于 B 的集合合并而成,故必然 < 2B。而当前 1 点所在的集合大小 < B,所以合并后 < 3B。

合并使用并查集即可,特殊点(原题为省会)的维护也是简单的(具体见代码吧)。时间复杂度 \(O(n\alpha(n))\)

\(\mathsf{\color{Thistle}Code}\)

#include <bits/stdc++.h>
#define fi first
#define se second
#define int long long

using namespace std;

typedef pair<int, int> PII;
typedef long long LL;

const int N = 1e4 + 10;

int n, m, B;
int par[N], sz[N], ctr[N], id[N];
std::vector<int> g[N];

int find(int x) {
	if (par[x] != x) par[x] = find(par[x]);
	return par[x];
}
void dfs1(int u, int fa) {
	vector<PII> tmp;
	for (auto v : g[u]) {
		if (v == fa) continue;
		dfs1(v, u);
		if (sz[find(v)] < B) tmp.push_back({sz[find(v)], v});
	}
	while (tmp.size() > 1) {
		auto a = tmp.back(), b = tmp[tmp.size() - 2];
		tmp.pop_back(), tmp.pop_back();
		int pa = find(a.se), pb = find(b.se);
		par[pa] = pb, sz[pb] += sz[pa];
		if (sz[pb] < B) tmp.push_back({sz[pb], b.se});
		else ctr[pb] = u;
	}
	if (tmp.size()) {
		int pa = find(u), pb = find(tmp[0].se);
		par[pa] = pb, sz[pb] += sz[pa];
		if (sz[pb] >= B) ctr[pb] = u;
	}
}
bool ok = 0;
void dfs2(int u, int fa) {
	if (ok) return;
	for (auto v : g[u]) {
		if (v == fa) continue;
		if (find(v) != find(u)) {
			par[find(v)] = find(u), ok = 1;
			return;
		}
		else dfs2(v, u);
		if (ok) return;
	}
}

signed main() {
	cin.tie(0);
	cout.tie(0);
	ios::sync_with_stdio(0);

	freopen("royal.in", "r", stdin);
	freopen("royal.out", "w", stdout);

	cin >> n >> B;
	for (int i = 1; i < n; i ++) {
		int u, v;
		cin >> u >> v, g[u].push_back(v), g[v].push_back(u);
	}
	for (int i = 1; i <= n; i ++ ) par[i] = i, sz[i] = 1;

	if (n <= 3 * B) {
		cout << 1 << endl;
		for (int i = 1; i <= n; i ++) cout << 1 << " ";
		cout << 1 << endl;
		return 0;
	}
	dfs1(1, -1);
	if (sz[find(1)] < B) dfs2(1, -1);

	for (int i = 1; i <= n; i ++)
		if (find(i) == i)
			id[find(i)] = ++ m;
	cout << m << endl;
	for (int i = 1; i <= n; i ++)
		cout << id[find(i)] << " ";
	cout << endl;
	for (int i = 1; i <= n; i ++)
		if (find(i) == i) {
			if (!ctr[i]) cout << i << " ";
			else cout << ctr[i] << " ";
		}

	return 0;
}
posted @ 2024-07-08 17:53  E-Syrus  阅读(7)  评论(0编辑  收藏  举报