1016: [JSOI2008]最小生成树计数
Time Limit: 1 Sec Memory Limit: 162 MBSubmit: 1577 Solved: 601
[ Submit][ Status][ Discuss]
Description
现在给出了一个简单无向加权图。你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的最小生成树。(如果两颗最小生成树中至少有一条边不同,则这两个最小生成树就是不同的)。由于不同的最小生成树可能很多,所以你只需要输出方案数对31011的模就可以了。
Input
第一行包含两个数,n和m,其中1<=n<=100; 1<=m<=1000; 表示该无向图的节点数和边数。每个节点用1~n的整数编号。 接下来的m行,每行包含两个整数:a, b, c,表示节点a, b之间的边的权值为c,其中1<=c<=1,000,000,000。数据保证不会出现自回边和重边。 注意:具有相同权值的边不会超过10条。
Output
输出不同的最小生成树有多少个。你只需要输出数量对31011的模就可以了。
Sample Input
4 6
1 2 1
1 3 1
1 4 1
2 3 2
2 4 1
3 4 1
1 2 1
1 3 1
1 4 1
2 3 2
2 4 1
3 4 1
Sample Output
8
Solution
最小生成树,由于相同权值的边不会很多,每次枚举相同的边中有哪些要选择即可,但要保证连通性不变。
Code
#include <cstdio>
#include <cstring>
#include <algorithm>
#define maxn 1005
#define mod 31011
using namespace std;
struct E {int from, to, tab;}edge[maxn];
int n, m, sz, tot;
int sum[maxn], st[maxn], a[maxn], a2[maxn], vis[maxn], temp, ans = 1;
bool cmp(E a, E b) {return a.tab < b.tab;}
int fa(int x)
{
if (a[x] == x) return x;
a[x] = fa(a[x]);
return a[x];
}
int fa2(int x) {if (a2[x] == x) return x; else return fa2(a2[x]);}
void Un(int x, int y)
{
int xx = fa(x), yy = fa(y);
if (xx != yy) a[xx] = yy;
}
void dfs(int st, int ed, int sum)
{
if (sum == 0) {++temp; return;}
if (st > ed) return;
int fx = fa2(edge[st].from), fy = fa2(edge[st].to);
if (vis[edge[st].from] && vis[edge[st].to] && fx != fy)
{
a2[fx] = fy;
dfs(st + 1, ed, sum - 1);
a2[fx] = fx;
}
dfs(st + 1, ed, sum);
}
void work(int x)
{
temp = 0;
dfs(st[x], st[x + 1] - 1, sum[x]);
ans = (ans * temp) % mod;
}
bool kruskal()
{
int i, j;
sort(edge + 1, edge + m + 1, cmp);
for (int i = 1; i <= n; i++) a[i] = a2[i] = i;
tot = sz = 0;
memset(vis, 0, sizeof(vis));
for (i = 1; i <= m; i++)
{
if (i == 1 || edge[i].tab != edge[i - 1].tab)
{
st[++tot] = i, sum[tot] = 0;
if (i != 1) work(tot - 1);
for (j = 1; j <= n; ++j) a2[j] = a[j];
}
if (fa(edge[i].from) != fa(edge[i].to))
{
++sz;
Un(edge[i].from, edge[i].to);
vis[edge[i].from] = vis[edge[i].to] = 1;
++sum[tot];
}
if (sz == n - 1) break;
}
for (j = i + 1; j <= m + 1; ++j)
if (edge[j].tab != edge[j - 1].tab) {st[tot + 1] = j; break;}
work(tot);
if (sz == n - 1) return 1; return 0;
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; i++)
scanf("%d %d %d", &edge[i].from, &edge[i].to, &edge[i].tab);
if (kruskal()) printf("%d\n", ans); else printf("0\n");
return 0;
}