洛谷 P8949 [YsOI2022] 淀粉树

洛谷传送门

考虑 \(d = 2\) 的部分分。相当于只用 \(2\) 次操作把 \(T\) 变成一条链。

不妨设最后变成的是一个 \(1 \sim n\) 的链,如果不是可以把点重编号。

第一次操作考虑以 \(n\) 为根,每次取每个儿子的子树中的最大值为新的根并和原来的根连边,这样会将整棵树具有堆的性质,即父亲的编号比儿子的编号大。

那么第二次操作时因为删完 \(1 \sim i - 1\) 的点后 \(i\) 一定是叶子,所以合法。

实现时发现第一次操作实际上是求点权 Kruskal 重构树,从小到大加点,并查集维护每个点所在子树的根即可。

\(d = 2\) 的部分分给我们启发。考虑若能让 \(S\) 逆操作 \(d - 2\) 次变成一条链,那么整道题就解决了。

考虑增量法,每次把 \(S\) 中的最大度数减 \(1\)。自底向上构造,若当前点的度数为最大度数,那么就断开它和父亲的连边,在子树里面随便找一个还没标记过的叶子,将父亲和这个叶子连边,并标记这个叶子。容易归纳证明每棵子树中未被标记的叶子个数 \(\ge 1\),所以不会出现所有叶子都被标记的情况。

时间复杂度 \(O(nd \log n)\)

code
// Problem: P8949 [YsOI2022] 淀粉树
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P8949
// Memory Limit: 256 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mkp make_pair
#define mems(a, x) memset((a), (x), sizeof(a))

using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
typedef long double ldb;
typedef pair<ll, ll> pii;

const int maxn = 200100;

int n, m, p[maxn], q[maxn], tim, fa[maxn];
vector<int> G[maxn], ans[maxn], leaf[maxn];
set<int> S[maxn], T[maxn];

void dfs(int u, int fa, int d) {
	ans[d][u] = fa;
	bool fl = 1;
	for (int v : S[u]) {
		if (v == fa) {
			continue;
		}
		fl = 0;
		dfs(v, u, d);
		if (leaf[u].size() < leaf[v].size()) {
			swap(leaf[u], leaf[v]);
		}
		for (int x : leaf[v]) {
			leaf[u].pb(x);
		}
		vector<int>().swap(leaf[v]);
	}
	if (fl) {
		leaf[u].pb(u);
		return;
	}
	if ((int)T[u].size() == d) {
		assert((int)leaf[u].size() >= 2);
		int v = leaf[u].back();
		leaf[u].pop_back();
		T[fa].insert(v);
		T[v].insert(fa);
		T[fa].erase(u);
		T[u].erase(fa);
	}
}

void dfs2(int u, int fa, int d) {
	ans[d][u] = fa;
	p[u] = ++tim;
	q[tim] = u;
	for (int v : S[u]) {
		if (v == fa) {
			continue;
		}
		dfs2(v, u, d);
	}
}

int find(int x) {
	return fa[x] == x ? x : fa[x] = find(fa[x]);
}

inline void merge(int x, int y) {
	x = find(x);
	y = find(y);
	if (x != y) {
		fa[x] = y;
	}
}

void solve() {
	scanf("%d%d", &n, &m);
	if (n == 2) {
		puts("1\n0 1");
		return;
	}
	for (int i = 1; i <= m; ++i) {
		ans[i] = vector<int>(n + 2);
	}
	for (int i = 1, u, v; i < n; ++i) {
		scanf("%d%d", &u, &v);
		G[u].pb(v);
		G[v].pb(u);
	}
	for (int i = 1, u, v; i < n; ++i) {
		scanf("%d%d", &u, &v);
		S[u].insert(v);
		S[v].insert(u);
	}
	int rt = 0;
	for (int i = 1; i <= n; ++i) {
		if ((int)S[i].size() == 1) {
			rt = i;
			break;
		}
	}
	for (int d = m; d >= 3; --d) {
		for (int i = 1; i <= n; ++i) {
			vector<int>().swap(leaf[i]);
			T[i] = S[i];
		}
		dfs(rt, 0, d);
		for (int i = 1; i <= n; ++i) {
			S[i] = T[i];
		}
	}
	dfs2(rt, 0, 2);
	for (int i = 1; i <= n; ++i) {
		fa[i] = i;
	}
	for (int i = 1; i <= n; ++i) {
		for (int j : G[q[i]]) {
			j = find(j);
			if (p[j] < i) {
				ans[1][j] = q[i];
				fa[j] = q[i];
			}
		}
	}
	printf("%d\n", m);
	for (int i = 1; i <= m; ++i) {
		for (int j = 1; j <= n; ++j) {
			printf("%d%c", ans[i][j], " \n"[j == n]);
		}
	}
}

int main() {
	int T = 1;
	// scanf("%d", &T);
	while (T--) {
		solve();
	}
	return 0;
}

posted @ 2024-04-10 16:38  zltzlt  阅读(59)  评论(0)    收藏  举报