【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

Sample Output

8

HINT

 

Source

 

 

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。