AtCoder Beginner Contest 262

E - Red and Blue Graph

https://atcoder.jp/contests/abc262/tasks/abc262_e

Time Limit: 2 sec / Memory Limit: 1024 MB

Score : 500 points

Problem Statement

You are given a simple undirected graph with N vertices and M edges. The vertices are numbered 1,,N, and the i-th (1iM) edge connects Vertices Ui and Vi.

There are 2N ways to paint each vertex red or blue. Find the number, modulo 998244353, of such ways that satisfy all of the following conditions:

  • There are exactly K vertices painted red.
  • There is an even number of edges connecting vertices painted in different colors.

Constraints

  • 2N2×105
  • 1M2×105
  • 0KN
  • 1Ui<ViN(1iM)
  • (Ui,Vi)(Uj,Vj)(ij)
  • All values in input are integers.

Input

Input is given from Standard Input in the following format:

N M K
U1 V1

UM VM

Output

Print the answer.


Sample Input 1

4 4 2
1 2
1 3
2 3
3 4

Sample Output 1

2

The following two ways satisfy the conditions.

  • Paint Vertices 1 and 2 red and Vertices 3 and 4 blue.
  • Paint Vertices 3 and 4 red and Vertices 1 and 2 blue.

In either of the ways above, the 2-nd and 3-rd edges connect vertices painted in different colors.


Sample Input 2

10 10 3
1 2
2 4
1 5
3 6
3 9
4 10
7 8
9 10
5 9
3 4

Sample Output 2

64

我的笔记

需要满足的要求是,有 K 个红点,且连接不同颜色点的边数为偶数,这样思考起来比较困难,需要进行转换。

我们可以思考红点的度数,所有红点度数之和 Dr 的组成有两种,一种是连接不同颜色点的边 Erb ,一种是连接两个红点的边 Err,用表达式表示即为:Dr=Erb+2Err

我们需要满足连接不同颜色点的边数为偶数,即 Erb 为偶数,通过上式即可知我们需要满足 Dr 为偶数。如此我们就将原问题转化为简单的顶点的度数问题。

我们首先读入边的情况,计算出每个点的度数,并计算出奇度、偶度顶点的个数,我们只需要这两个数据。

由于我们要求 Dr 为偶数,那么红偶度顶点没有限制,而红奇度顶点的个数必须为偶数,通过组合公式计算即可。

另外,由于组合公式 Cnm=n!m!(nm)!,取模时需要用到模逆元,即变为 Cnm=n!inv(m!)inv((nm)!),可以预先计算出阶乘加快速度。

代码

#include <bits/stdc++.h>
#define int long long

using namespace std;

const int MOD = 998244353;
const int MAXN = 2e5 + 10;
int fact[MAXN], invf[MAXN];

int fast_pow(int a, int b)
{
    a %= MOD;
    int ans = 1;
    while (b)
    {
        if (b % 2 == 1)
            ans = ans * a % MOD;
        a = a * a % MOD;
        b >>= 1;
    }
    return ans;
}

void fact_init()
{
    fact[0] = 1;
    for (int i = 1; i <= MAXN; i++)
        fact[i] = fact[i - 1] * i % MOD;
    for (int i = 0; i <= MAXN; i++)
        invf[i] = fast_pow(fact[i], MOD - 2);
}

int comb(int n, int m)
{
    return ((fact[n] * invf[m]) % MOD * invf[n - m]) % MOD;
}

signed main()
{
    fact_init();
    int N, M, K;
    cin >> N >> M >> K;
    vector<int> d(N);
    for (int i = 0; i < M; i++)
    {
        int U, V;
        cin >> U >> V;
        d[U - 1]++;
        d[V - 1]++;
    }
    int odd = 0;
    for (auto x : d)
        if (x % 2)
            odd++;
    int even = N - odd;
    long long ans = 0;
    for (int selected_odd = 0; selected_odd <= K; selected_odd += 2)
    {
        int selected_even = K - selected_odd;
        if (selected_odd <= odd && selected_even <= even)
        {
            ans += comb(odd, selected_odd) * comb(even, selected_even);
            ans %= MOD;
        }
    }
    cout << ans << endl;
    return 0;
}