2019 China Collegiate Programming Contest Qinhuangdao Onsite F. Forest Program(DFS计算图中所有环的长度)

题目链接:https://codeforces.com/gym/102361/problem/F

题意

\(n\) 个点和 \(m\) 条边,每条边属于 \(0\)\(1\) 个环,问去掉一些边使得图变为森林的方案个数。

题解

找出所有环的长度 \(c_i\),每个环可以去掉 \(1,2,\dots,c_i\) 条边,方案各为 \(C_{c_i}^1,C_{c_i}^2, \dots C_{c_i}^{c_i}\),即 \(2^{c_i} - 1\)
所有环的去边方案共有 \(\prod \limits _{i = 1}^{c.size} 2^{c_i} - 1\)
余下的边为 \(m - \sum \limits _{i=1}^{c.size} c_i\),可以去掉 \(0,1,2,\dots,m - \sum \limits _{i=1}^{c.size} c_i\) 条边,方案总数为 \(2^{m - \sum \limits _{i=1}^{c.size} c_i}\)
所以总的方案个数即为:\({2^{m - \sum \limits _{i=1}^{c.size} c_i}} \times ({\prod \limits _{i = 1}^{c.size} 2^{c_i} - 1})\)
下面就是如何计算所有环的长度,利用 \(dfs\) 记录每个点的深度:

  • 如果下一个点深度小于当前点且访问过,那么二者深度之差加一即为所在环的长度。
  • 如果下一个点深度大于等于当前点,则该点之前已访问过且环的长度也已经计算过,可以跳过。

Tips

图可能不连通,所以需要对每个未访问过的点都进行 \(dfs\)

代码

#include <bits/stdc++.h>
using namespace std;
constexpr int MOD = 998244353;

int binpow(int a, int b) {
    int res = 1;
    while (b) {
        if (b & 1) res = 1LL * res * a % MOD;
        a = 1LL * a * a % MOD;
        b >>= 1;
    }
    return res;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n, m;
    cin >> n >> m;
    vector<vector<int>> G(n);
    for (int i = 0; i < m; i++) {
        int u, v;
        cin >> u >> v;
        --u, --v;
        G[u].push_back(v);
        G[v].push_back(u);
    }
    vector<int> dep(n), cycle;
    function<void(int, int)> dfs = [&](int u, int p) {
        if (dep[u] != 0) {
            if (dep[u] < dep[p])
                cycle.push_back(dep[p] - dep[u] + 1);
        } else {
            dep[u] = (p == -1 ? 0 : dep[p]) + 1;
            for (auto v : G[u]) {
                if (v != p) {
                    dfs(v, u);
                }
            }
        }
    };
    for (int i = 0; i < n; i++) {
        if (dep[i] == 0) {
            dfs(i, -1);
        }
    }
    long long ans = binpow(2, m - accumulate(cycle.begin(), cycle.end(), 0));
    for (auto i : cycle) {
        ans *= binpow(2, i) - 1;
        ans %= MOD;
    }
    cout << ans << "\n";
    return 0;
}
posted @ 2020-10-21 22:45  Kanoon  阅读(200)  评论(0编辑  收藏  举报