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;
}

三军可夺帅也,匹夫不可夺志也。

posted @ 2024-08-01 13:28  FRZ_29  阅读(1)  评论(0编辑  收藏  举报