BZOJ 2582 : Bovine Alliance

2582: [Usaco2012Jan]Bovine Alliance

>原题链接<

Description

给出n个点m条边的图,现把点和边分组,每条边只能和相邻两点之一分在一组,点可以单独一组,问分组方案数。

Input

* Line 1: Two space-separated integers N and M

 * Lines 2..1+M: Line i+1 describes the ith trail. Each line contains two space-separated integers u_i and v_i (1 <= u_i, v_i <= N, u_i != v_i) describing the pair of farms connected by the trail.  Note that there can be two trails between the same pair of farms.

Output

* Line 1: A single line containing the number of assignments of trails to farms, taken modulo 1,000,000,007. If no assignment satisfies the above conditions output 0.

Sample Input

5 4
1 2
3 2
4 5
4 5

Sample Output

6

思路 :

  本题理清思路考虑就好了,因为集合只存在两种情况 即 只有一个点,或者 一个点配一个边, 那么只需要对每个联通块分情况考虑 。若V<E 那么肯定有边不在集合里,直接输出0

若V==E,则只要确定该联通块和哪个点分组,整个联通块的分组情况就确定了,而一条边只会有两种分组情况,故对答案的贡献为2, 如果V==E+1 则联通块是个树,那么任选一个点为根,都有一种方案,故对答案的贡献为V。

  而具体维护的方法我们可以采用并查集(Tarjan肯定也是可以的,但我没有写)来维护一下联通块的信息,最后把每个联通块的ANS乘到一起就是答案,别忘了mod1e9+7哦!

下面附上代码

  

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
const int p = 1e9+7;
int fa[110000],psiz[110000],esiz[110000];
long long ans;
struct node {
	int s,t;
}e[110000];
int find(int x) {
	return fa[x]==x?x:fa[x]=find(fa[x]);
}
int main() {
	int n, m, s, t;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
		fa[i]=i,psiz[i]=1;
	for(int i=1;i<=m;i++) {
		scanf("%d%d",&s,&t);
		int dx=find(s),dy=find(t);
		if(dx!=dy)
			fa[dx]=dy,
			esiz[dy]+=esiz[dx]+1,
			psiz[dy]+=psiz[dx];
		else esiz[dx]++;
	}
	ans=1;
	for(int i=1;i<=n;i++)
		if(fa[i]==i)
			if(psiz[i]==esiz[i]+1)
				(ans*=psiz[i])%=p;
			else if(psiz[i]==esiz[i])
				(ans*=2)%=p;
			else {
				puts("0");
				return 0;
			}
	printf("%lld\n",ans%p);
}

 欢迎来原Blog看看 >原文链接<

猜你喜欢

转载自www.cnblogs.com/Tobichi/p/9078805.html