P4208 [JSOI2008]最小生成树计数
题目描述
现在给出了一个简单无向加权图。你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的最小生成树。(如果两颗最小生成树中至少有一条边不同,则这两个最小生成树就是不同的)。由于不同的最小生成树可能很多,所以你只需要输出方案数对\(31011\)的模就可以了。
题解
容易想到对于边权相同的那些边,选出来的边数是一定的,所以最终答案其实就是各个边权相同的部分的方案数的乘积,用并查集维护\(dfs\)即可,注意:由于有撤销操作,所以不能进行路径压缩。
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 1005;
const int mod = 31011;
int n, m, fa[N], cnt, tot, sum, ans = 1, now;
struct node{int x, y, z;}a[N];
struct data{int l, r, val;}e[N];
inline int read()
{
int x = 0, f = 1; char ch = getchar();
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
return x * f;
}
bool cmp(const node & a, const node & b) {return a.z < b.z;}
int find(int x) {return fa[x] == x ? x : find(fa[x]);}
void dfs(int x, int goal, int num)
{
if(x == goal + 1) {if(num == now) sum ++; return;}
int xx = find(a[x].x), yy = find(a[x].y);
if(xx != yy)
{
fa[xx] = yy; dfs(x + 1, goal, num + 1);
fa[xx] = xx; fa[yy] = yy;
}
dfs(x + 1, goal, num);
}
int main()
{
// freopen("a.in", "r", stdin);
// freopen("a.out", "w", stdout);
n = read(); m = read();
for(int i = 1; i <= n; i ++) fa[i] = i;
for(int i = 1; i <= m; i ++) a[i].x = read(), a[i].y = read(), a[i].z = read();
sort(a + 1, a + m + 1, cmp);
for(int i = 1; i <= m; i ++)
{
if(a[i].z != a[i - 1].z) cnt ++, e[cnt].l = i, e[cnt - 1].r = i - 1;
int x = find(a[i].x), y = find(a[i].y);
if(x != y) {fa[x] = y; tot ++; e[cnt].val ++;}
}
e[cnt].r = m; if(tot != n - 1) {printf("0\n"); return 0;}
for(int i = 1; i <= n; i ++) fa[i] = i;
for(int i = 1; i <= cnt; i ++)
{
sum = 0; now = e[i].val; dfs(e[i].l, e[i].r, 0); ans = (ans * sum) % mod;
for(int j = e[i].l; j <= e[i].r; j ++)
{
int x = find(a[j].x), y = find(a[j].y);
if(x != y) fa[x] = y;
}
}
printf("%d\n", ans);
return 0;
}