CodeForces 1830D Mex Tree

发布时间 2023-05-29 14:03:09作者: zltzlt

洛谷传送门

CF 传送门

考虑答案的下界。

对整棵树进行二分图染色,我们得到答案是 \(n \times (n + 1) - O(n)\) 级别的。但是二分图染色不一定是最优的。

考虑计算 \(O(n)\) 的损失。发现在极大同色连通块内的点对才会造成损失,并且因为损失是 \(O(n)\) 的,所以极大同色连通块大小是 \(O(\sqrt{n})\) 的。

考虑 dp,设 \(f_{u,0/1,i}\) 为包含 \(u\) 的极大颜色为 \(0/1\) 的连通块大小是 \(i\) 的最少损失。转移就按照直接树形背包合并就行。注意我们在 \(v \to u\)\(u,v\) 颜色不同时计算 \(v\) 的连通块的贡献。

本题卡空间,因此需要对于每个 \(f_{u,0/1,i}\) 开大小为 \(\min(sz_u, \sqrt{n})\) 的 vector,并且合并完立刻销毁。

code
// Problem: D. Mex Tree
// Contest: Codeforces - Codeforces Round 875 (Div. 1)
// URL: https://codeforces.com/contest/1830/problem/D
// Memory Limit: 256 MB
// Time Limit: 3000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

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

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

const int maxn = 200100;
const int B = 450;
const int inf = 0x3f3f3f3f;

int n, sz[maxn], g[maxn];
vector<int> G[maxn], f[maxn][2];

void dfs(int u, int fa) {
	vector<int>(2).swap(f[u][0]);
	vector<int>(2).swap(f[u][1]);
	sz[u] = 1;
	for (int v : G[u]) {
		if (v == fa) {
			continue;
		}
		dfs(v, u);
		for (int x = 0; x <= 1; ++x) {
			f[u][x].resize(min(B, sz[u] + sz[v]) + 1);
			for (int i = 1; i <= min(B, sz[u] + sz[v]); ++i) {
				g[i] = f[u][x][i];
				f[u][x][i] = inf;
			}
			for (int y = 0; y <= 1; ++y) {
				for (int i = 1; i <= min(B, sz[u]); ++i) {
					for (int j = 1; j <= min(B, sz[v]); ++j) {
						if (x ^ y) {
							f[u][x][i] = min(f[u][x][i], g[i] + f[v][y][j] + j * (j + 1) / 2 * (y ? 2 : 1));
						} else if (i + j <= B) {
							f[u][x][i + j] = min(f[u][x][i + j], g[i] + f[v][y][j]);
						}
					}
				}
			}
		}
		sz[u] += sz[v];
		vector<int>().swap(f[v][0]);
		vector<int>().swap(f[v][1]);
	}
}

void solve() {
	scanf("%d", &n);
	for (int i = 1; i <= n; ++i) {
		vector<int>().swap(G[i]);
	}
	for (int i = 1, u, v; i < n; ++i) {
		scanf("%d%d", &u, &v);
		G[u].pb(v);
		G[v].pb(u);
	}
	dfs(1, -1);
	int ans = inf;
	for (int i = 1; i <= min(sz[1], B); ++i) {
		for (int x = 0; x <= 1; ++x) {
			ans = min(ans, f[1][x][i] + i * (i + 1) / 2 * (x ? 2 : 1));
		}
	}
	printf("%lld\n", 1LL * n * (n + 1) - ans);
}

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