Blog of RuSun

\begin {array}{c} \mathfrak {One Problem Is Difficult} \\\\ \mathfrak {Because You Don't Know} \\\\ \mathfrak {Why It Is Diffucult} \end {array}

P4221 [WC2018]州区划分

P4221 [WC2018]州区划分

一个选择集合合法当且仅当不存在一个欧拉回路(所有点的度数都为偶数)或者不连通(并查集判断),预处理出所有集合的 $g _ i = (\sum _ {x \in i} w _ x) ^ p$ 。

考虑 DP ,$f _ i$ 表示集合 $i$ 内所有划分的答案的和,那么则有:
$$
f _ i = \sum _ {j \in i} f _ j \times \frac {g _ {i - j}} {g _ i}
$$
将所有 $g _ i$ 提出来,发现是子集卷积。但是是 $f _ i$ 与前面 $cnt$ 更小的 $f _ j$ 卷,所以对于每一个 $cnt$ 就卷一次后还原将 $g _ i$ 更新上,如果 $cnt \neq i$ ,一定要去除,然后在做 fwt 卷起来。

查看代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
#include <cstdio>
using namespace std;
typedef long long LL;
template <class Type>
void read(Type &x)
{
char c;
bool flag = false;
while ((c = getchar()) < '0' || c > '9')
c == '-' && (flag = true);
x = c - '0';
while ((c = getchar()) >= '0' && c <= '9')
x = (x << 3) + (x << 1) + c - '0';
flag && (x = ~x + 1);
}
template <class Type>
void write(Type x)
{
x < 0 && (putchar('-'), x = ~x + 1);
x > 9 && (write(x / 10), 0);
putchar(x % 10 + '0');
}
const int N = 21, M = 1 << N, K = 220, mod = 998244353;
int cnt[M], inv[M], g[N + 1][M], f[N + 1][M];
int n, m, p, w[N];
struct Edge
{
int u, v;
} e[K];
int binpow (int b, int k = mod - 2)
{
int res = 1;
while (k)
{
k & 1 && (res = (LL)res * b % mod);
b = (LL)b * b % mod;
k >>= 1;
}
return res;
}
namespace DSU
{
int p[N], d[N];
void init()
{
for (int i = 0; i < n; i++)
p[i] = i, d[i] = 0;
}
int fa (int x)
{
return p[x] == x ? x : p[x] = fa(p[x]);
}
void merge (int x, int y)
{
d[x]++, d[y]++;
p[fa(x)] = fa(y);
}
bool check (int x)
{
int s = 0;
for (int i = 0; i < n; i++)
s += p[i] == i;
if (s > n - x + 1)
return true;
for (int i = 0; i < n; i++)
if (d[i] & 1)
return true;
return false;
}
}
void fwt (int *x, int op)
{
for (int mid = 1; mid < 1 << n; mid <<= 1)
for (int i = 0; i < 1 << n; i += mid << 1)
for (int j = 0; j < mid; j++)
(x[i | j | mid] += x[i | j] * op) %= mod;
}
int main ()
{
read(n), read(m), read(p);
for (int i = 1; i <= m; i++)
{
read(e[i].u), read(e[i].v);
e[i].u--, e[i].v--;
}
for (int i = 0; i < n; i++)
read(w[i]);
for (int i = 0; i < 1 << n; i++)
{
int tot = 0;
for (int j = 0; j < n; j++)
tot += (i >> j & 1) * w[j];
DSU::init();
for (int j = 1; j <= m; j++)
i >> e[j].u & 1 && i >> e[j].v & 1 && (DSU::merge(e[j].u, e[j].v), 0);
cnt[i] = cnt[i >> 1] + (i & 1);
if (DSU::check(cnt[i]))
g[cnt[i]][i] = binpow(tot, p);
inv[i] = binpow(binpow(tot, p));
}
for (int i = 0; i <= n; i++)
fwt(g[i], 1);
f[0][0] = 1;
fwt(f[0], 1);
for (int i = 1; i <= n; i++)
{
for (int j = 0; j <= i - 1; j++)
for (int k = 0; k < 1 << n; k++)
(f[i][k] += (LL)f[j][k] * g[i - j][k] % mod) %= mod;
fwt(f[i], -1);
for (int j = 0; j < 1 << n; j++)
f[i][j] = cnt[j] == i ? (LL)f[i][j] * inv[j] % mod : 0;
i ^ n && (fwt(f[i], 1), 0);
}
write((f[n][(1 << n) - 1] + mod) % mod);
return 0;
}