最大生成树计数

最大生成树计数

最近做到了一道题目,有关于最大生成树计数的,这里来说一下,求解最大生成树数量的方法。

题意

给你一个 \(n\) 个点 \(m\) 条边的无向图,每个边有权值,求出来这个图里,最大生成树的个数。

答案对于 \(998244353\) 取模。

题解

我们考虑一个事情,就是说,对于给定的图的任意的一个最大生成树,对于一个固定的权值 \(w\),他们对应的权值为 \(w\) 的边的个数相同。

所以,对于每个权值,分别做,计数就好了。

我们考虑已经做了一些大的权值,然后现在要做 \(w\) 权值的边的计数。

我们首先,对于之前的那些大于 \(w\) 的边,会形成一些连通块,我们对于 \(w\) 的边,只做连接两个不同连通块的边所构成的生成树的计数(这里把之前的一个连通块看成了一个点),然后用矩阵树定理得到答案即可。

需要注意的是,我们可能做到 \(w\) 的时候图是不连通的,所以,需要对于每个大于等于 \(w\) 的边的连通块分别运用矩阵树定理求解即可。

这里给出一道例题。

【2022省选十连测 Day 3】treecnt - 题目 - Zhengrui Online Judge (zhengruioi.com)

在这道题目中,我们只要对于两个点 \(x,y\) 之间,连一条 \(w(i,j)\) 的边,其中 \(w(i,j)\) 为有多少个限制里同时包含 \(i\) 点和 \(j\) 点。然后,我们一个符合要求的生成树就是权值为 \(\sum S_i - K\) 的生成树,然后同时这个生成树权值肯定最大,所以,我们对于最大生成树计数即可。

#include <bits/stdc++.h>
const int MAXN = 505, MOD = 998244353;
using std::cin;
using std::cout;
using std::bitset;
using std::sort;
using std::vector;
using std::swap;
struct Edge {
	int x, y, w, key;
} e[MAXN * MAXN];
int N, K, ecnt, f1[MAXN], f2[MAXN], tot, id[MAXN], A[MAXN][MAXN];
char s[MAXN];
bitset<2010> bs[MAXN];
vector<Edge> v[MAXN * MAXN];
int find1(int x) {
	return f1[x] == x ? x : f1[x] = find1(f1[x]);
}
int find2(int x) {
	return f2[x] == x ? x : f2[x] = find2(f2[x]);
}
auto Mod = [] (int x) {
	if (x >= MOD) {
		return x - MOD;
	}
	else if (x < 0) {
		return x + MOD;
	}
	else {
		return x;
	}
};
auto Ksm = [] (int x, int y) -> int {
	int ret = 1;
	for (; y; y >>= 1, x = (long long) x * x % MOD) {
		if (y & 1) {
			ret = (long long) ret * x % MOD;
		}
	}
	return ret;
};
int det(int m) {
	int ret = 1;
	for (int i = 1; i <= m; ++i) {
		for (int j = i; j <= m; ++j) {
			if (A[j][i]) {
				for (int k = i; k <= m; ++k) {
					swap(A[i][k], A[j][k]);
				}
				if (j != i) {
					ret = Mod(-ret);
				}
				break;
			}
		}
		ret = (long long) ret * A[i][i] % MOD;
		int invl = Ksm(A[i][i], MOD - 2);
		for (int j = i + 1; j <= m; ++j) {
			if (A[j][i]) {
				int mul = (long long) invl * A[j][i] % MOD;
				for (int k = i; k <= m; ++k) {
					A[j][k] = Mod(A[j][k] - (long long) mul * A[i][k] % MOD);
				}
			}
		}
	}
	return ret;
}
int calc(int x) {
	int cnt = 0;
	for (auto &i: v[x]) {
		if (!id[find2(i.x)]) {
			id[f2[i.x]] = ++cnt;
		}
		if (!id[find2(i.y)]) {
			id[f2[i.y]] = ++cnt;
		}
		int x = id[f2[i.x]], y = id[f2[i.y]];
		A[x][x] = Mod(A[x][x] + i.w);
		A[y][y] = Mod(A[y][y] + i.w);
		A[x][y] = Mod(A[x][y] - i.w + MOD);
		A[y][x] = Mod(A[y][x] - i.w + MOD);
	}
	for (auto &i: v[x]) {
		id[f2[i.x]] = 0;
		id[f2[i.y]] = 0;
	}
	for (auto &i: v[x]) {
		if (find2(i.x) != find2(i.y)) {
			f2[f2[i.x]] = f2[i.y];
		}
	}
	int ret = det(cnt - 1);
	for (int i = 1; i <= cnt; ++i) {
		for (int j = 1; j <= cnt; ++j) {
			A[i][j] = 0;
		}
	}
	return ret;
}
int main() {
	std::ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> N >> K;
	for (int i = 1, x; i <= N - 1; ++i) {
		for (int j = i + 1; j <= N; ++j) {
			cin >> x;
			e[++ecnt] = {i, j, x, 0};
		}
	}
	int S = 0;
	for (int i = 1; i <= K; ++i) {
		cin >> s + 1;
		for (int j = 1; j <= N; ++j) {
			bs[j].set(i, s[j] == '1');
			S += s[j] == '1';
		}
	}
	for (int i = 1; i <= ecnt; ++i) {
		e[i].key = (bs[e[i].x] & bs[e[i].y]).count();
	}
	for (int i = 1; i <= N; ++i) {
		f1[i] = i;
	}
	sort(e + 1, e + 1 + ecnt, [&] (const Edge &a, const Edge &b) -> int {
		return a.key > b.key;
	});
	int singercoder = 0;
	for (int i = 1; i <= ecnt; ++i) {
		if (find1(e[i].x) != find1(e[i].y)) {
			f1[f1[e[i].x]] = f1[e[i].y];
			singercoder += e[i].key;
		}
	}
	if (singercoder != S - K) {
		cout << 0 << '\n';
		return 0;
	}
	for (int i = 1; i <= N; ++i) {
		f1[i] = f2[i] = i;
	}
	int ANS = 1;
	for (int i = 1, p; i <= ecnt; i = p + 1) {
		p = i;
		while (p < ecnt && e[p + 1].key == e[i].key) {
			++p;
		}
		for (int j = i; j <= p; ++j) {
			if (find1(e[j].x) != find1(e[j].y)) {
				f1[f1[e[j].x]] = f1[e[j].y];
			}
		}
		for (int j = i; j <= p; ++j) {
			if (find2(e[j].x) == find2(e[j].y)) {
				continue;
			}
			if (!id[find1(e[j].x)]) {
				id[find1(e[j].x)] = ++tot;
			}
			v[id[f1[e[j].x]]].push_back(e[j]);
		}
		for (int j = i; j <= p; ++j) {
			id[f1[e[j].x]] = 0;
		}
		for (int j = 1; j <= tot; ++j) {
			ANS = (long long) ANS * calc(j) % MOD;
			v[j].clear();
		}
	}
	cout << ANS << '\n';
	return 0;
}
上一篇:循环队列


下一篇:多校省选模拟4