【BZOJ】1016: [JSOI2008]最小生成树计数(kruskal+特殊的技巧)
http://www.lydsy.com/JudgeOnline/problem.php?id=1016
想也想不到QAQ
首先想不到的是:题目有说,具有相同权值的边不会超过10条。
其次:老是去想组合计数怎么搞。。。。。。。于是最sb的暴力都不会了。。
所以这题暴力搞就行了orz
依次加边,每一种边的方案数乘起来就是方案了。
注意并查集不能路径压缩,否则在计数的时候会waQAQ因为并查集的路径压缩是不可逆的QAQ
#include <cstdio> #include <cstring> #include <cmath> #include <string> #include <iostream> #include <algorithm> #include <queue> #include <set> #include <map> using namespace std; typedef long long ll; #define pii pair<int, int> #define mkpii make_pair<int, int> #define pdi pair<double, int> #define mkpdi make_pair<double, int> #define pli pair<ll, int> #define mkpli make_pair<ll, int> #define rep(i, n) for(int i=0; i<(n); ++i) #define for1(i,a,n) for(int i=(a);i<=(n);++i) #define for2(i,a,n) for(int i=(a);i<(n);++i) #define for3(i,a,n) for(int i=(a);i>=(n);--i) #define for4(i,a,n) for(int i=(a);i>(n);--i) #define CC(i,a) memset(i,a,sizeof(i)) #define read(a) a=getint() #define print(a) printf("%d", a) #define dbg(x) cout << (#x) << " = " << (x) << endl #define error(x) (!(x)?puts("error"):0) #define printarr2(a, b, c) for1(_, 1, b) { for1(__, 1, c) cout << a[_][__]; cout << endl; } #define printarr1(a, b) for1(_, 1, b) cout << a[_] << '\t'; cout << endl inline const int getint() { int r=0, k=1; char c=getchar(); for(; c<'0'||c>'9'; c=getchar()) if(c=='-') k=-1; for(; c>='0'&&c<='9'; c=getchar()) r=r*10+c-'0'; return k*r; } inline const int max(const int &a, const int &b) { return a>b?a:b; } inline const int min(const int &a, const int &b) { return a<b?a:b; } const int N=105, M=1005, MD=31011; int n, m, cnt, p[N], ans=1, sum; struct dat { int x, y, w; }e[M], a[M]; inline const bool cmp(const dat &a, const dat &b) { return a.w<b.w; } inline const int ifind(const int &x) { return x==p[x]?x:ifind(p[x]); } void dfs(int now, int s, const int &x) { if(now>a[x].y) { if(s==a[x].w) ++sum; return; } dfs(now+1, s, x); int fx=ifind(e[now].x), fy=ifind(e[now].y); if(fx!=fy) { p[fx]=fy; dfs(now+1, s+1, x); p[fx]=fx; p[fy]=fy; } } int main() { read(n); read(m); for1(i, 1, m) read(e[i].x), read(e[i].y), read(e[i].w); for1(i, 1, n) p[i]=i; sort(e+1, e+1+m, cmp); int ed=0; for1(i, 1, m) { if(e[i].w!=e[i-1].w) a[++cnt].x=i, a[cnt-1].y=i-1; int fx=ifind(e[i].x), fy=ifind(e[i].y); if(fx!=fy) { p[fx]=fy; ++a[cnt].w; ++ed; } } if(ed!=n-1) { puts("0"); return 0; } a[cnt].y=m; for1(i, 1, n) p[i]=i; for1(i, 1, cnt) { sum=0; dfs(a[i].x, 0, i); ans=(ans*sum)%MD; for1(j, a[i].x, a[i].y) { int fx=ifind(e[j].x), fy=ifind(e[j].y); if(fx!=fy) p[fx]=fy; } } print(ans); return 0; }
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
HINT
Source
博客地址:www.cnblogs.com/iwtwiioi 本文为博主原创文章,未经博主允许不得转载。一经发现,必将追究法律责任。