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\) 一定不能是桥边。接下来分类讨论:
- \(E1=E2\),对答案贡献 \(2^{|E| - |V| + k - 1}\)。
- \(E1\neq E2\),且 \(E1,E2\) 不在同一个连通块。此时连通块个数不变,对答案贡献 \(2^{|E| - |V| +k - 2}\)。
- \(E1\neq E2\),且 \(E1,E2\) 在同一个连通块,并且增加了新的连通块。对答案贡献 \(2^{|E| - |V| +k - 1}\)
- \(E1\neq E2\),且 \(E1,E2\) 在同一个连通块,并且没有增加新的连通块。对答案贡献 \(2^{|E| - |V| +k - 2}\)
情况 \(1,2\) 容易计算,并且 \(4\) 在计算了 \(3\) 之后也容易计算,考虑如何计算 \(3\)。
- 树边 \(+\) 非树边。此时该树边只能被这一条非树边覆盖。
- 树边 \(+\) 树边。此时覆盖这两条树边的非树边集合相同。我们给每条非树边随机一个权值,树边的权值为覆盖其所有非树边权值的异或和。
代码
#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;
}

浙公网安备 33010602011771号