Loading

P3043 [USACO12JAN] Bovine Alliance G (并查集)

P3043 [USACO12JAN] Bovine Alliance G

并查集

每个连通块方案数独立。考虑一个连通块的情况,显然如果 \(m>n\) 一定无解,那么就只有 \(m=n\)\(m=n-1\) 两种情况,前者是基环树,后者是树。

基环树的环上,第一条边选择的端点确定,其他也就确定,共有两种情况(顺时针和逆时针)。环下的树选择固定。所以总方案数为 \(2\) 种。

树上所有边的端点选择完后,会剩下唯一的节点,将这个节点作为根,那么子树的选择也是固定的。由于这唯一的节点任意,所以方案数就是连通块的节点数。

那么就可以维护连通块内边数和点数计算答案了,用并查集即可。

复杂度 \(O(n\log n)\)

#include <bits/stdc++.h>
#define pii std::pair<int, int>
#define mk std::make_pair
#define fi first
#define se second
#define pb push_back

using i64 = long long;
using ull = unsigned long long;
const i64 iinf = 0x3f3f3f3f, linf = 0x3f3f3f3f3f3f3f3f;
const int N = 1e5 + 10, mod = 1e9 + 7;
int n, m;
i64 ans = 1;
int fa[N], cnt[N], sz[N];
int find(int x) {
	if(x != fa[x]) fa[x] = find(fa[x]);
	return fa[x];
}
void merge(int x, int y) {
	int fx = find(x), fy = find(y);
	if(fx == fy) cnt[fy]++;
	else {
		fa[fx] = fy, cnt[fy] += cnt[fx] + 1;
		sz[fy] += sz[fx];
	}
}
int vis[N];
int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
	std::cin >> n >> m;

	for(int i = 1; i <= n; i++) fa[i] = i, sz[i] = 1;
	for(int i = 1; i <= m; i++) {
		int u, v;
		std::cin >> u >> v;
		merge(u, v);
	}

	for(int i = 1; i <= n; i++) {
		int x = find(i);
		if(!vis[x]) {
			if(cnt[x] > sz[x]) ans = 0;
			else if(cnt[x] == sz[x]) ans = ans * 2 % mod;
			else ans = ans * sz[x] % mod;
			vis[x] = 1;
		}
	}

	std::cout << ans << "\n";

	return 0;
}
posted @ 2024-07-08 21:20  Fire_Raku  阅读(6)  评论(0编辑  收藏  举报