2019 CCPC 秦皇岛F Forest Program(dfs)

传送门

题意:给定一张无向简单图,同时规定一条边只属于一个环。可以删除任意条边使得这张图变成森林,也就是使得每一个连通块都是树。求一共有多少种方案。

分析:由于原题规定一条边只属于一个环,不需要考虑环套环。每一种方案删除之后不能存在环,所以对于图中所有环,设环的边数为s,删除边的数量从1,2,3……s都是合法的,所以对于一个环的方案数为2^s-1。对于许多环,方案数相乘取模。同时,非环边可以任意删,所以求出所有环之后,设非环边数量为t,删除环边总方案为ans,删除非环边方案为2^t,则最后答案应当是2^t*ans。以上所有运算取模。

找环可以使用dfs一遍求出。方法为:vis数组设置为三种状态,0表示未被访问过。1表示正在被访问,即边指向的结点是当前结点在dfs树上的祖先节点。2表示访问完毕。同时dfs的同时记录每一个结点的先驱path。如果边访问到了vis为1的数组,说明存在环,则通过path数组,从当前结点回跳到指向的结点,经过的步数为环的长度-1。

代码:

#include 
#define mp make_pair
#define debug(x) cout << #x << ": " << x << endl
#define pb push_back
typedef long long LL;
const int maxn = 3e5 + 10;
const int inf = 0x3f3f3f3f;
const LL INF = 0x3f3f3f3f3f3f3f3f;
using namespace std;
int u, v, n, m, path[maxn], vis[maxn];
LL ans = 1, cnt, mod = 998244353, sum = 0;
vector<int> g[maxn];
LL qpow(LL x) {
    LL res = 1, temp = 2;
    while (x) {
        if (x & 1) {
            res = (res * temp) % mod;
        }
        temp = (temp * temp) % mod;
        x >>= 1;
    }
    return res % mod;
}
void dfs(int now, int pre) {
    vis[now] = 1;
    for (int i = 0; i < g[now].size(); ++i) {
        int to = g[now][i];
        if (to == pre)
            continue;
        if (vis[to] == 0) {
            path[to] = now;
            dfs(to, now);
        }
        else if (vis[to] == 2) {
            continue;
        }
        else {
            int temp = now;
            cnt = 1;
            while (temp != to) {
                ++cnt;
                temp = path[temp];
            }
            sum += cnt;
            ans = (ans * (qpow(cnt) - 1)) % mod;
        }
    }
    vis[now] = 2;
}
int main() {
    scanf("%d %d", &n, &m);
    for (int i = 1; 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] == 0) {
            dfs(i, 0);
        }
    }
    printf("%lld\n", qpow(m - sum) * ans % mod);

}
View Code

 

你可能感兴趣的:(2019 CCPC 秦皇岛F Forest Program(dfs))