题目链接: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\) 记录每个点的深度:
图可能不连通,所以需要对每个未访问过的点都进行 \(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;
}
手机扫一扫
移动阅读更方便
你可能感兴趣的文章