题目大意:求一张 $n$ 个点 $m$ 条边的无向图的最小生成树个数,保证具有相同权值的边不超过 $10$ 条。$n \leq 100, m \leq 1000$
用到一个性质:对于每一棵最小生成树,每种权值的边的数量一定;连完每种权值的边后,图的连通情况相同。
由于具有相同权值的边不超过 $10$ 条,可以 $dfs$ 计算每种权值的边的连接方案数。
#include <cstdio>
#include <algorithm>
const int mod = 31011;
struct Edge {
int u, v, w;
bool operator < (const Edge &rhs) const {
return w < rhs.w;
}
} e[1005];
struct Node {
int l, r, n;
} a[1005];
int fa[105], n, m, cnt, sum, ans = 1;
int find(int x) { //不路径压缩, 便于分裂
return x == fa[x] ? x : find(fa[x]);
}
void dfs(int cur, int k, int tot) {
if (cur > a[k].r) {
sum += tot == a[k].n;
return;
}
dfs(cur + 1, k, tot);
int fx = find(e[cur].u), fy = find(e[cur].v);
if (fx != fy) {
fa[fx] = fy;
dfs(cur + 1, k, tot + 1);
fa[fx] = fx;
}
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; ++i)
scanf("%d%d%d", &e[i].u, &e[i].v, &e[i].w);
std::sort(e + 1, e + m + 1);
for (int i = 1; i <= n; ++i) fa[i] = i;
a[++cnt].l = 1;
for (int i = 1; i <= m; ++i) {
int fx = find(e[i].u), fy = find(e[i].v);
if (fx != fy) fa[fx] = fy, ++a[cnt].n, ++sum;
if (e[i].w != e[i+1].w)
a[cnt].r = i, a[++cnt].l = i + 1;
}
if (sum != n - 1) { puts("0"); return 0; }
for (int i = 1; i <= n; ++i) fa[i] = i;
for (int i = 1; i <= cnt; ++i) {
sum = 0;
dfs(a[i].l, i, 0);
if (sum) (ans *= sum) %= mod;
for (int j = a[i].l; j <= a[i].r; ++j) {
int fx = find(e[j].u), fy = find(e[j].v);
if (fx != fy) fa[fx] = fy;
}
}
printf("%d\n", ans);
return 0;
}
时间复杂度 $O(\dfrac{2^{10}nm}{10})$