bzoj1016: [JSOI2008]最小生成树计数
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,0
00。数据保证不会出现自回边和重边。注意:具有相同权值的边不会超过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
HINT
Source
因为相同权值的边的个数不超过10,那么我们可以发现最小生成树在某一个权值选的边的个数肯定是固定的,那么我们就可以把权值分开,然后乘法原理,枚举每条边选不选,并查集维护一下连通性。
复杂度O((m*n+m*2^10)*log m)。
之前WA了好多次主要是因为没有判图连不连通,不连通的话答案是0。
还有就是选择边的边界小错误。注意一下。
代码:
路径压缩:
#include <cstdio> #include <cstring> #include <algorithm> #include <cstdlib> #include <iostream> const int mod = 31011; int n, m; struct edge { int u, v, w; bool operator < (const edge &that) const { return w < that.w; } } e[1010]; bool cho[1010]; struct ufset { int fa[110]; int find(int x) { return x == fa[x] ? x : fa[x] = find(fa[x]); } void uni(int x, int y) { x = find(x), y = find(y); if(x == y) return; fa[x] = y; } void init(int n) { for(int i = 1; i <= n; i++) fa[i] = i; } } s, ss; int cnt, end, ans = 1, res; void dfs(int now, int x, ufset s) { if(now > end) { if(x >= cnt) res++; return; } dfs(now+1, x, s); if(s.find(e[now].u) != s.find(e[now].v)) { s.uni(e[now].u, e[now].v); dfs(now+1, x+1, s); } } int main() { scanf("%d%d", &n, &m); s.init(n); 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); int tot = 0; for(int i = 1; i <= m; i++) { if(s.find(e[i].u) != s.find(e[i].v)) { s.uni(e[i].u, e[i].v); cho[i] = 1; tot++; } } if(tot != n-1) { puts("0"); return 0; } for(int i = 1, j = 0; i <= m; i = j+1) { int x = 0; while(e[j+1].w == e[i].w) x += cho[++j]; ss.init(n); for(int k = 1; k <= m; k++) if(cho[k] && e[k].w != e[i].w) ss.uni(e[k].u, e[k].v); cnt = x, end = j, res = 0; dfs(i, 0, ss); ans = ans*res%mod; } printf("%d\n", ans); return 0; }
按秩合并:
#include <cstdio> #include <cstring> #include <algorithm> #include <cstdlib> #include <iostream> const int mod = 31011; int n, m; struct edge { int u, v, w; bool operator < (const edge &that) const { return w < that.w; } } e[1010]; bool cho[1010]; struct ufset { int fa[110], siz[110]; int find(int x) { return x == fa[x] ? x : find(fa[x]); } void uni(int x, int y) { x = find(x), y = find(y); if(x == y) return; if(siz[x] < siz[y]) std::swap(x, y); fa[x] = y; siz[x] += siz[y]; } void init(int n) { for(int i = 1; i <= n; i++) fa[i] = i, siz[i] = 1; } } s, ss; int cnt, end, ans = 1, res; void dfs(int now, int xx) { if(now > end) { if(xx >= cnt) res++; return; } dfs(now+1, xx); if(ss.find(e[now].u) != ss.find(e[now].v)) { int x = ss.find(e[now].u), y = ss.find(e[now].v); ss.uni(x, y); dfs(now+1, xx+1); if(ss.siz[x] > ss.siz[y]) ss.siz[x] -= ss.siz[y]; else ss.siz[y] -= ss.siz[x]; ss.fa[x] = x, ss.fa[y] = y; } } int main() { scanf("%d%d", &n, &m); s.init(n); 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); int tot = 0; for(int i = 1; i <= m; i++) { if(s.find(e[i].u) != s.find(e[i].v)) { s.uni(e[i].u, e[i].v); cho[i] = 1; tot++; } } if(tot != n-1) { puts("0"); return 0; } for(int i = 1, j = 0; i <= m; i = j+1) { int x = 0; while(e[j+1].w == e[i].w) x += cho[++j]; ss.init(n); for(int k = 1; k <= m; k++) if(cho[k] && e[k].w != e[i].w) ss.uni(e[k].u, e[k].v); cnt = x, end = j, res = 0; dfs(i, 0); ans = ans*res%mod; } printf("%d\n", ans); return 0; }