【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

 

 

你可能感兴趣的:(最小生成树)