[ARC087F] Squirrel Migration

模拟赛题,不知道为什么放在最后一题,感觉评分过高了。

首先是经典问题,最大值是 \(\sum \min(siz, n-siz)\),证明考虑每条边上界。

考虑证明的构造,对于重心的每个儿子拉出子树,求出子树大小即可转为序列上问题:每个点不跟内部匹配即可满足最大,容易证明充要。

朴素 dp 发现怎么也避不开两维状态,非常没有前途。

考虑二项式反演,钦定 \(x\) 个点与自己匹配,剩下的方案是好算的,于是 \(f_{i,j}\) 表示考虑前 \(i\) 个子树,其中钦定的有 \(j\) 个的方案,转移枚举这个子树钦定几个,方案数是 \(\binom{siz}{k}^2*fac_k\),统计答案时对于剩下的 \(n-j\) 个点可以随便匹配。

复杂度是 \(O(n^2)\),证明考虑类似树形背包,任意两个点只会被算一次。

const int N = 5005;
int n;
vector <int> e[N];
int A, B, siz[N], rt, rt_siz;
void find_rt(int u, int fa) {
	siz[u] = 1;
	int mx = 0;
	for (auto v : e[u]) if (v != fa) {
		find_rt(v, u);
		siz[u] += siz[v];
		ckmax(mx, siz[v]);
	}
	ckmax(mx, n - siz[u]);
	if (!rt || mx < rt_siz) rt = u, rt_siz = mx;
}
void dfs(int u, int fa) {
	siz[u] = 1;
	for (auto v : e[u]) if (v != fa) dfs(v, u), siz[u] += siz[v];
}
int w[N], tot;
int f[N], g[N], fac[N], inv[N];
void init(int lim) {
	fac[0] = 1;
	for (int i = 1; i <= lim; i++) fac[i] = Cmul(fac[i - 1], i);
	inv[lim] = ksm(fac[lim], mod - 2);
	for (int i = lim; i >= 1; i--) inv[i - 1] = Cmul(inv[i], i);
}
int binom(int n, int m) {
	if (n < m || m < 0) return 0;
	return Cmul(fac[n], inv[m], inv[n - m]);
}

void main01() {
	ios::sync_with_stdio(0), cin.tie(0);
	cin >> n;
	for (int i = 1, u, v; i < n; i++) {
		cin >> u >> v;
		e[u].emplace_back(v);
		e[v].emplace_back(u);
	}
	find_rt(1, 0);
	for (auto v : e[rt]) dfs(v, rt), w[++tot] = siz[v];
	init(n);
	f[0] = 1;
	for (int o = 1, sum = 0, siz; o <= tot; o++) {
		siz = w[o];
		for (int j = 0; j <= sum; j++) if (f[j]) {
			for (int k = 0; k <= siz; k++) {
				Madd(g[j + k], Cmul(f[j], binom(siz, k), binom(siz, k), fac[k]));
			}
		}
		sum += siz;
		for (int j = 0; j <= sum; j++) f[j] = g[j], g[j] = 0;
	}
	for (int i = 0; i <= n; i++) Mmul(f[i], fac[n - i]);
	int ans = 0;
	for (int i = 0; i <= n; i++) {
		if (i & 1) Mdel(ans, f[i]);
		else Madd(ans, f[i]);
	}
	cout << ans << '\n';
}
posted @ 2024-11-06 15:19  Anonymely  阅读(28)  评论(0)    收藏  举报