求无向图环长我所知道的有两种解法,第一种就是染色,第二种是双连通分量(不会)。
题意:给出一个图,可以删除任意条边(可不删),删除一些边后,让这个图变成森林(连通块全部都是树)。求有多少种删法。
思路:
1.如果是一个环且环长为len,那么必须删除一条边,所以删法就有2n - 1组合数学。
2.如果不形成环,那么就可以不删除,删法就有2n种。
所以现在我们只需要求出每个环的长度(第一种情况),m - 所有环的总长度(第二种情况)。最后用快速幂算出即可。
怎样求环的长度。
我们可以用个vis数组进行标记,vis = 0表示还没有访问的结点,vis = 1表示正在访问的结点,vis = 2表示访问完毕的结点。用pre数组记录i的父亲节点是谁,当枚举的结点vis = 1时,就形成环,此时用pre数组回溯即可求出环长。
AC代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 3e5 + 5;
const int mod = 998244353;
vector<int> vt, G[maxn];
int pre[maxn],vis[maxn];
ll fast_power(ll a, ll b) {
ll res = 1;
while (b > 0) {
if (b & 1) {
res = res * a % mod;
}
b >>= 1;
a = a * a % mod;
}
return res % mod;
}
//vis = 1表示正在走, vis = 0表示没走过, vis = 2表示已走完
void dfs(int u, int fa) {
vis[u] = 1;
int i, v, len = G[u].size();
for (int i = 0; i < len; ++i) {
v = G[u][i];
if (v == fa) {
//保证结点一直在往前走,而不是往后走
continue;
}
if (vis[v] == 0) {
//未被访问就继续搜索,并记录其结点关系
pre[v] = u;
dfs(v, u);
} else if (vis[v] == 1) {
//如果有被访问过,就形成环
int t = u;
int cnt = 1;
while (t != v) {
cnt++;
t = pre[t];
}
vt.push_back(cnt);
}
}
vis[u] = 2;
}
void solve() {
int n, m, u, v;
scanf("%d%d", &n, &m);
for (int i = 0; i < m; ++i) {
scanf("%d%d", &u, &v);
G[u].push_back(v);
G[v].push_back(u);
}
for (int i = 1; i <= n; ++i) {
if (!vis[i]) {
dfs(i, -1);
}
}
int len = vt.size();
ll ans = 1, cnt = 0;
for (int i = 0; i < len; ++i) {
cnt += vt[i];
ans = ans * (fast_power(2, vt[i]) - 1) % mod;
}
if (m - cnt > 0) {
ans = ans * fast_power(2, m - cnt) % mod;
}
printf("%lld\n", ans % mod);
}
int main(){
int t;
solve();
return 0;
}