P3043 [USACO12JAN] Bovine Alliance G
题目传送门
思路
首先分情况讨论每种联通块的可能,有三种不同的情况会对答案 \(ans\) 产生不同的贡献。
联通块有环
如图,因为每条边都有要有归属,所以环上的边只能全都顺时针或逆时针属于某个点,且不在环上的点仅有一种可能。
因此该情况对答案的贡献为 \(ans \times 2\) 。
联通块为链
对于一条链,边数比点数小 1 所以仅有一个点是没有边的,而若确认某个点没有边的话,边的所属仅有一种可能。因此答案的贡献为 \(ans \times t\) , \(t\) 为该联通块点的个数。
联通块内边数大于等于点数
问题的答案为 \(0\) ,直接结束了。
转化
问题变为判断联通块内的情况,对此,利用联通块内度数除以 \(2\) 等于边数,判断点数与边数的关系即可。
代码
#include <iostream>
#include <cstdio>
typedef long long LL;using namespace std;const int mod = 1e9 + 7, N = 1e5 + 5;#define MOD(x) ((x + mod) % mod)
#define LF(i, __l, __r) for (int i = __l; i <= __r; i++)
#define RF(i, __r, __l) for (int i = __r; i >= __l; i--)int head[N], ver[N * 2], nxt[N * 2], edge[N * 2];
int n, m, tot = 1;
bool vis[N];
LL ans = 1, cnt, cnt1;void add(int u, int v) {ver[++tot] = v;nxt[tot] = head[u], head[u] = tot;
}void dfs(int u) {vis[u] = true, cnt++;for (int i = head[u]; i; i = nxt[i]) {cnt1++;if (!vis[ver[i]]) dfs(ver[i]);}
}int main() {scanf("%d%d", &n, &m);LF(i, 1, m) {int x, y;scanf("%d%d", &x, &y);add(x, y), add(y, x);}LF(i, 1, n) {if (!vis[i]) {cnt = cnt1 = 0;dfs(i);if (cnt == cnt1 / 2) ans = MOD(ans * 2);else if (cnt > cnt1 / 2) ans = MOD(ans * cnt);else { ans = 0; break; } }}printf("%lld", ans);return 0;
}
三军可夺帅也,匹夫不可夺志也。