1016: [JSOI2008]最小生成树计数

1016: [JSOI2008]最小生成树计数

Time Limit: 1 Sec   Memory Limit: 162 MB
Submit: 4162   Solved: 1673
[ 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

Sample Output

8

HINT

Source

[ Submit][ Status][ Discuss]



暴力大法好。。。


对于最小生成树,同一种权值边使用次数一样

因为在两种方案里若同种权值边使用次数不同,必有一种更优

然后每种边不超过10条

每种爆搜即可


#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<queue>
using namespace std;

typedef long long LL;
const LL mo = 31011;
const int maxn = 1E2 + 10;
const int maxm = maxn*10;

struct E{
	int u,v,w;
	bool operator < (const E&b) const {
		return w < b.w;
	}
}e[maxn*10];

int n,m,cur = 1,fa[maxn],use[maxm],num[maxm];
int a[maxm],po;
LL ans = 1,sum;

int father(int k)
{
	return k == fa[k]?k:father(fa[k]);
}

void dfs(int x,int tot)
{
	if (x == a[po]+1) {
		if (tot == use[po]) ++sum; 
		return;
	}
	if (a[po] - x + 1 + tot < use[po]) return;
	
	int fu = father(e[x].u);
	int fv = father(e[x].v);
	if (fu != fv) {
		fa[fu] = fv;
		dfs(x+1,tot+1);
		fa[fu] = fu;
	}
	dfs(x+1,tot);
}

int main()
{
	#ifdef YZY
		   freopen("yzy.txt","r",stdin);
	#endif
	
	cin >> n >> m;
	for (int i = 1; i <= m; i++) {
		int u,v,w;
		scanf("%d%d%d",&u,&v,&w);
		e[i] = (E){u,v,w};
		num[i] = w;
	}
	for (int i = 1; i <= n; i++) fa[i] = i;
	sort(e+1,e+m+1);
	sort(num+1,num+m+1);
	for (int i = 2; i <= m; i++)
		if (num[i] != num[i-1])
			num[++cur] = num[i]; 
	
	int tot = 0;
	for (int i = 1; i <= m; i++) {
		int fu = father(e[i].u);
		int fv = father(e[i].v);
		int pos = lower_bound(num+1,num+cur+1,e[i].w) - num;
		if (fu != fv) {
			fa[fu] = fv;
			++tot;
			++use[pos];
		}
		if (e[i].w != e[i-1].w) a[pos-1] = i-1;
	}
	a[cur] = m;
	if (tot != n-1) {
		cout << 0;
		return 0;
	}
	
	for (int i = 1; i <= n; i++) fa[i] = i;
	for (int i = 1; i <= m; i++)
		if (e[i].w != e[i-1].w) {
			sum = 0;
			po = lower_bound(num+1,num+cur+1,e[i].w) - num;
			dfs(i,0);
			ans = ans*sum%mo;
			for (int j = i; j <= a[po]; j++) {
				int fu = father(e[j].u);
				int fv = father(e[j].v);
				if (fu != fv) fa[fu] = fv;
			}
		}
	cout << ans;
	return 0;
}


你可能感兴趣的:(1016: [JSOI2008]最小生成树计数)