2017集训队作业自选题#108欧拉子图

题意

定义无向图 \(G\)\(Eularion\) 的,当且仅当其每个连通子图都存在欧拉回路。

你有一个 \(n\) 个点 \(m\) 条边的无向图,设 \(S\) 为边集 \(E\) 的一个子集。 若只考虑 \(S\) 中的边整个图是 \(Eulerian\) 的则 \(f(S) = |S|^2\),否则\(f(S) = 0\)。求所有边的子集 \(S\)\(f(S)\) 之和。

\(n, m\leq 2\times 10 ^5\)

题解

\(f(S)= [S 是 Eularion 的]\) ,则 \(\sum \limits _{S\in E} f(S) = 2 ^ {|E| - |V| + k}\)。其中 \(k\)\(G\) 的连通块个数。

证明:考虑 \(G\) 是连通的情况,找出一棵生成树。对于每种非树边的选择方式,有且仅有一种树边的选择方式满足条件。

考虑把平方拆开:\(\sum \limits _{E1} \sum \limits _{E2}\sum \limits _{S\in E} [E1,E2\in S] f(S)\),相当于我们枚举两条边必选。

注意到我们选的边 \(E1, E2\) 一定不能是桥边。接下来分类讨论:

  1. \(E1=E2\),对答案贡献 \(2^{|E| - |V| + k - 1}\)
  2. \(E1\neq E2\),且 \(E1,E2\) 不在同一个连通块。此时连通块个数不变,对答案贡献 \(2^{|E| - |V| +k - 2}\)
  3. \(E1\neq E2\),且 \(E1,E2\) 在同一个连通块,并且增加了新的连通块。对答案贡献 \(2^{|E| - |V| +k - 1}\)
  4. \(E1\neq E2\),且 \(E1,E2\) 在同一个连通块,并且没有增加新的连通块。对答案贡献 \(2^{|E| - |V| +k - 2}\)

情况 \(1,2\) 容易计算,并且 \(4\) 在计算了 \(3\) 之后也容易计算,考虑如何计算 \(3\)

  1. 树边 \(+\) 非树边。此时该树边只能被这一条非树边覆盖。
  2. 树边 \(+\) 树边。此时覆盖这两条树边的非树边集合相同。我们给每条非树边随机一个权值,树边的权值为覆盖其所有非树边权值的异或和。

代码

#include <bits/stdc++.h>

const int N = 2e5 + 10, M = 4e5 + 10, Mod = 998244353;

inline int read()
{
    int x = 0;
    char ch = getchar();
    while (!isdigit(ch)) ch = getchar();
    while (isdigit(ch)) x = x * 10 + ch - '0', ch = getchar();
    return x;
}

inline int Pow(int a, int b)
{
    if (b < 0) return 0;
    int res = 1;
    for (; b; b >>= 1, a = 1LL * a * a % Mod)
        if (b & 1) res = 1LL * res * a % Mod;
    return res;
}

namespace RAND
{
    unsigned long long seed = 13244074693642402ull;

    inline unsigned long long Rand()
    {
        seed ^= seed << 13, seed ^= seed >> 7, seed ^= seed << 17;
        return seed;
    }
}

int tot = 1, fir[N], nex[M], got[M];
int idx = 0, dfn[N], low[N], ont[M];
int cnt = 0, a[N], BridgeNum, EdgeNum, Numno;
unsigned long long b[N], vec[N];

inline void AddEdge(int u, int v)
{
    nex[++tot] = fir[u], fir[u] = tot, got[tot] = v;
}

inline void dfs(int u, int fa)
{
    dfn[u] = low[u] = ++idx;
    for (int i = fir[u]; ++EdgeNum, i; i = nex[i]) if (!dfn[got[i]]) 
    {
        dfs(got[i], i), low[u] = std::min(low[u], low[got[i]]);
        ont[i] = true, BridgeNum += low[got[i]] > dfn[u];
    }
    else if (i != (fa ^ 1)) 
    {
        low[u] = std::min(low[u], dfn[got[i]]);
        if (dfn[u] < dfn[got[i]])
        {
            unsigned long long w = RAND::Rand();
            b[got[i]] ^= w, b[u] ^= w, a[got[i]]++, a[u]--;
		}
    }
}

inline void dfs(int u)
{
    for (int i = fir[u]; i; i = nex[i]) if (ont[i])
        dfs(got[i]), a[u] += a[got[i]], b[u] ^= b[got[i]];
    Numno += a[u] == 1;
    if (b[u]) vec[cnt++] = b[u];
}

int main()
{
    int n = read(), m = read();
    for (int i = 1; i <= m; ++i)
    {
        int u = read(), v = read();
        AddEdge(u, v), AddEdge(v, u);
    }
    int res1 = 0, res2 = 0, res3 = 0, res4 = 0, k = 0, sum = 0;
    for (int i = 1; i <= n; ++i) if (!dfn[i])
    {
        BridgeNum = EdgeNum = Numno = idx = cnt = 0, dfs(i, 0), dfs(i), ++k;
        int w = (EdgeNum - idx) / 2 - BridgeNum;
        int W = 1LL * w * (w - 1) / 2 % Mod, num = 1, s = 0;
        (res1 += w) %= Mod;
        (res4 += 1LL * sum * w % Mod) %= Mod;
        std::sort(vec, vec + cnt);
        for (int i = 1; i < cnt; ++i)
            if (vec[i] == vec[i - 1]) (s += num) %= Mod, ++num;
            else num = 1;
        (s += Numno) %= Mod, (res3 += s) %= Mod, (res2 += W - s) %= Mod;
        sum += w;
    }
    std::cerr << res1 << ' ' << res2 << ' ' << res3 << ' ' << res4 << std::endl;
    (res1 += 2LL * res3 % Mod) %= Mod;
    (res2 <<= 1) %= Mod, (res2 += 2LL * res4 % Mod) %= Mod;
    int ans1 = 1LL * res1 * Pow(2, m - n + k - 1) % Mod;
    int ans2 = 1LL * res2 * Pow(2, m - n + k - 2) % Mod;
    printf("%d\n", (ans1 + ans2) % Mod);
    return 0;
}
posted @ 2020-10-20 20:58  bo1949  阅读(146)  评论(0)    收藏  举报