[BZOJ 1016] [JSOI2008] 最小生成树计数 【DFS】

题目链接:BZOJ - 1016

 

题目分析

最小生成树的两个性质:

同一个图的最小生成树,满足:

1)同一种权值的边的个数相等

2)用Kruscal按照从小到大,处理完某一种权值的所有边后,图的连通性相等

这样,先做一次Kruscal求出每种权值的边的条数,再按照权值从小到大,对每种边进行 DFS, 求出这种权值的边有几种选法。

最后根据乘法原理将各种边的选法数乘起来就可以了。

特别注意:在DFS中为了在向下DFS之后消除决策影响,恢复f[]数组之前的状态,在DFS中调用的Find()函数不能路径压缩。 

 

代码

#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <cstdio>
#include <algorithm>

using namespace std;

const int MaxN = 100 + 5, MaxM = 1000 + 5, Mod = 31011;

int n, m, Top, Sum, Cnt;
int f[MaxN], L[MaxM], R[MaxM], Num[MaxM];

typedef long long LL;
LL Ans;

struct Edge
{
	int u, v, w; 
} E[MaxM];

inline bool Cmp(Edge e1, Edge e2) 
{
	return e1.w < e2.w;
}

inline int Find(int x, int o) 
{
	int i, j, k;
	j = x; 
	while (j != f[j]) j = f[j];
	if (o == 1) return j;
	i = x;
	while (i != j) 
	{
		k = i;
		i = f[i];
		f[k] = j;
	}
	return j;
}

void DFS(int Type, int x, int y) 
{
	if (x == R[Type] + 1) 
	{
		if (y == Num[Type]) ++Cnt;
		return;
	}
	int fx, fy;
	fx = Find(E[x].u, 1); fy = Find(E[x].v, 1);
	if (fx != fy)
	{
		f[fx] = fy;
		DFS(Type, x + 1, y + 1);
		f[fx] = fx;
	}
	DFS(Type, x + 1, y);
}

int main() 
{
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= m; ++i)
		scanf("%d%d%d", &E[i].u, &E[i].v, &E[i].w);
	sort(E + 1, E + m + 1, Cmp);
	Top = 0; Sum = 0;
	int fx, fy;
	for (int i = 1; i <= n; ++i) f[i] = i;
	for (int i = 1; i <= m; ++i) 
	{
		if (i == 1 || E[i].w != E[i - 1].w) L[++Top] = i;
		R[Top] = i;
		fx = Find(E[i].u, 0); fy = Find(E[i].v, 0);
		if (fx != fy) 
		{
			f[fx] = fy;
			++Num[Top];
			++Sum;
		}
	}
	if (Sum != n - 1) 
	{
		printf("0\n");
		return 0;
	}
	for (int i = 1; i <= n; ++i) f[i] = i;
	Ans = 1;
	for (int i = 1; i <= Top; ++i) 
	{
		if (Num[i] == 0) continue;
		Cnt = 0;
		DFS(i, L[i], 0);
		Ans = Ans * (LL)Cnt % Mod;
		for (int j = L[i]; j <= R[i]; ++j) 
		{
			fx = Find(E[j].u, 0); fy = Find(E[j].v, 0);
			if (fx != fy) f[fx] = fy;
		}
	}
	printf("%d\n", (int)Ans);
	return 0;
}

  

posted @ 2015-03-06 21:26  JoeFan  阅读(...)  评论(... 编辑 收藏