BZOJ 2582 [Usaco2012Jan]Bovine Alliance

BZOJ_2582

    首先来说,如果一个连通块里面的边的数量EN如果大于点的数量VN的话,那么显然是没办法保证每条边都属于一个顶点的,于是接下来就只剩两种情况要讨论了,一种是EN==VN,一种是EN==VN-1。

    如果EN==VN,说明这个连通块里存在一个环,而环上的点是必须要选环上的边的,否则环上的边就会剩下,这样不妨把环拿下来,这时就会发现剩下的部分的分配方案是唯一的,而环这部分的分配方案是有且只有2种的。

    如果EN==VN-1,可能着眼于哪个点拥有哪条边不是那么好计算,但换个思路就会非常清晰了,由于VN<EN,那么最后会有一个孤立点,我们不妨考虑这个孤立点是哪个点,而一旦确定某个点是孤立点之后,就可以把这个点拿出来了,这时就会发现剩下的部分的分配方案是唯一的,于是这种情况总的分配方案就是VN。

#include<stdio.h>
#include<string.h>
#define MAXD 100010
#define MAXM 200010
#define MOD 1000000007
typedef long long LL;
int N, M, first[MAXD], e, next[MAXM], v[MAXM], vis[MAXD], cal[MAXM];
int VN, EN, ANS;
void add(int x, int y)
{
    v[e] = y;
    next[e] = first[x], first[x] = e ++;    
}
void init()
{
    memset(first, -1, sizeof(first[0]) * (N + 1)), e = 0;
    for(int i = 0; i < M; i ++)
    {
        int x, y;
        scanf("%d%d", &x, &y);
        add(x, y), add(y, x);    
    }
}
void dfs(int cur)
{
    vis[cur] = 1, ++ VN;
    for(int i = first[cur]; i != -1; i = next[i])
        if(!cal[i])
        {
            cal[i] = cal[i ^ 1] = 1, ++ EN;
            if(!vis[v[i]]) dfs(v[i]);    
        }    
}
void solve()
{
    ANS = 1;
    memset(vis, 0, sizeof(vis[0]) * (N + 1));
    memset(cal, 0, sizeof(cal[0]) * e);
    for(int i = 1; i <= N; i ++)
        if(!vis[i])
        {
            VN = EN = 0;
            dfs(i);
            if(EN > VN)
            {
                printf("0\n");
                return ;    
            }
            if(EN == VN) ANS = ANS * 2 % MOD;
            else ANS = (LL)ANS * VN % MOD;
        }
    printf("%d\n", ANS);
}
int main()
{
    while(scanf("%d%d", &N, &M) == 2)
    {
        init();
        solve();    
    }
    return 0;    
}
原文地址:https://www.cnblogs.com/staginner/p/2712350.html