题意:求一个n(1<=n<=100)个点,m(1<=m<=1000)条边的图的最小生成树数量,其中,相同权值的边不会超过10条。
分析:
首先我们需要知道关于MST的两个性质:
1.所有MST中,相同权值的边的出现次数相同。
2.对于某一相同权值的边,添加其出现次数次这条边并保证它达到最大效率,图的连通性不变。
所以我们需要用一遍kruskal求出每条边的出现次数,通过dfs枚举这些边的出现方法,需要注意的是dfs中并查集不能用路径压缩,因为这样回溯时没有办法修改。
#include <cstdio>
#include <cstring>
#include <algorithm>
const int N = 105, M = 1005, p = 31011;
int n,m,ans=1,tt,anss,bian,f[N];
struct nd {
int x, y, z;
bool operator < (const nd &rhs) const {return z < rhs.z;}
}a[M];
struct hh {int l,r,cnt;}b[M];
int fnd(int x) {return f[x] == x ? x : f[x] = fnd(f[x]);}
int fnd2(int x) {return f[x] == x ? x : fnd2(f[x]);}
void dfs(int x, int y, int z) {
if(y == b[x].r+1) {
if(z == b[x].cnt) anss++;
return;
}
int u = fnd2(a[y].x), v = fnd2(a[y].y);
if(u != v) {
f[u] = v;
dfs(x, y+1, z+1);
f[u] = u;
}
dfs(x, y+1, z);
}
int main() {
scanf("%d%d", &n, &m);
for(int i = 1; i <= m; i++) scanf("%d%d%d", &a[i].x, &a[i].y, &a[i].z);
std::sort(a+1, a+1+m);
for(int i = 1; i <= n; i++) f[i] = i;
for(int i = 1; i <= m; i++) {
if(a[i].z != a[i-1].z) b[tt++].r = i-1, b[tt].l = i;
if(fnd(a[i].x) != fnd(a[i].y)) b[tt].cnt++, f[fnd(a[i].x)] = fnd(a[i].y), bian++;
}
b[tt].r = m;
if(bian != n-1) {puts("0"); return 0;}
for(int i = 1; i <= n; i++) f[i] = i;
for(int i = 1; i <= tt; i++) {
anss = 0;
dfs(i, b[i].l, 0);
ans = ans*anss%p;
for(int j = b[i].l; j <= b[i].r; j++) if(fnd(a[j].x) != fnd(a[j].y))
f[fnd(a[j].x)] = fnd(a[j].y);
}
printf("%d", ans);
return 0;
}