Address
洛谷P4221
BZOJ5153
UOJ#348
LOJ#2340
Solution
有一个显然的 dp 方案:
f
[
S
]
f[S]
f[S] 表示选出的城市集合为
S
S
S 的满意度之和。
s
u
m
[
S
]
sum[S]
sum[S] 表示城市集合
S
S
S 的人口之和。
g
[
S
]
g[S]
g[S] 表示:
[
子
图
S
不
存
在
欧
拉
回
路
]
×
s
u
m
[
S
]
p
[子图S不存在欧拉回路]\times sum[S]^p
[子图S不存在欧拉回路]×sum[S]p
转移显然:
f
[
S
]
=
1
s
u
m
[
S
]
p
∑
T
⊊
S
f
[
T
]
g
[
S
−
T
]
f[S]=\frac1{sum[S]^p}\sum_{T\subsetneq S}f[T]g[S-T]
f[S]=sum[S]p1T⊊S∑f[T]g[S−T]
复杂度
O
(
3
n
)
O(3^n)
O(3n) ,难以通过
n
=
21
n=21
n=21 的数据。
考虑到这是子集卷积的形式,不妨把
f
f
f 按照
S
S
S 集合大小分类:
f
[
i
]
[
S
]
f[i][S]
f[i][S] 表示:
{
f
[
S
]
∣
S
∣
=
i
0
ELSE
\begin{cases}f[S]&|S|=i\\0&\text{ELSE}\end{cases}
{f[S]0∣S∣=iELSE
那从小到大枚举
i
i
i ,再枚举一个
0
≤
j
<
i
0\le j<i
0≤j<i ,那么
f
[
i
]
f[i]
f[i] 就能从
f
[
j
]
f[j]
f[j] 和
g
[
i
−
j
]
g[i-j]
g[i−j] 转移。
如果已经求得了
f
[
0...
i
−
1
]
f[0...i-1]
f[0...i−1] 的快速莫比乌斯变换
f
[
0
]
′
,
f
[
1
]
′
,
.
.
.
,
f
[
i
−
1
]
′
f[0]',f[1]',...,f[i-1]'
f[0]′,f[1]′,...,f[i−1]′ ,
以及
g
g
g 的 FMT
g
′
g'
g′ ,
那么就可以直接计算
f
[
i
]
′
[
S
]
+
=
f
[
j
]
′
[
S
]
×
g
[
i
−
j
]
′
[
S
]
f[i]'[S]+=f[j]'[S]\times g[i-j]'[S]
f[i]′[S]+=f[j]′[S]×g[i−j]′[S] 。
反演回去之后将每个
f
[
i
]
[
S
]
f[i][S]
f[i][S] 除以
s
u
m
[
S
]
p
sum[S]^p
sum[S]p 。
注意需要将
∣
S
∣
≠
i
|S|\ne i
∣S∤=i 的
f
[
i
]
[
S
]
f[i][S]
f[i][S] 清零。
复杂度
O
(
2
n
×
n
2
)
O(2^n\times n^2)
O(2n×n2) 。
Code
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
#define Rof(i, a, b) for (i = a; i >= b; i--)
#define Subset(k, i) for (k = i; k; k = (k - 1) & i)
using namespace std;
inline int read()
{
int res = 0; bool bo = 0; char c;
while (((c = getchar()) < '0' || c > '9') && c != '-');
if (c == '-') bo = 1; else res = c - 48;
while ((c = getchar()) >= '0' && c <= '9')
res = (res << 3) + (res << 1) + (c - 48);
return bo ? ~res + 1 : res;
}
const int N = 25, C = (1 << 21) + 5, MOD = 998244353;
int qpow(int a, int b)
{
int res = 1;
while (b)
{
if (b & 1) res = 1ll * res * a % MOD;
a = 1ll * a * a % MOD;
b >>= 1;
}
return res;
}
int n, m, p, Cm, w[N], cnt[N], f[N][C], sum[C], inv[C], ans, eul[N][C],
sze[C], fa[N];
bool gr[N][N], cir[C];
void FWT(int n, int *a, int op)
{
int i, j;
For (i, 1, n) For (j, 0, (1 << n) - 1)
if (!((j >> i - 1) & 1))
a[j | (1 << i - 1)] = (a[j | (1 << i - 1)]
+ (op == 1 ? a[j] : MOD - a[j])) % MOD;
}
int fd(int x)
{
if (fa[x] != x) fa[x] = fd(fa[x]);
return fa[x];
}
void mg(int x, int y)
{
int ix = fd(x), iy = fd(y);
if (ix != iy) fa[iy] = ix;
}
int main()
{
int i, j, S, x, y;
n = read(); m = read(); p = read();
Cm = (1 << n) - 1;
For (i, 1, m) x = read(), y = read(),
gr[x][y] = gr[y][x] = 1;
For (i, 1, n) w[i] = read();
For (S, 1, Cm)
{
For (i, 1, n) cnt[i] = 0, fa[i] = i; int xp = 0;
For (i, 1, n) if ((S >> i - 1) & 1)
{
sum[S] += w[i]; sze[S]++; xp = i;
For (j, i + 1, n) if (((S >> j - 1) & 1) && gr[i][j])
cnt[i]++, cnt[j]++, mg(i, j);
}
sum[S] = qpow(sum[S], p);
inv[S] = qpow(sum[S], MOD - 2);
For (i, 1, n) if (((S >> i - 1) & 1) && fd(i) != fd(xp))
{cir[S] = 1; continue;}
For (i, 1, n) if (cnt[i] & 1)
{cir[S] = 1; continue;}
}
For (i, 0, Cm) eul[sze[i]][i] = cir[i] * sum[i];
For (i, 0, n) FWT(n, eul[i], 1);
f[0][0] = 1;
FWT(n, f[0], 1);
For (i, 1, n)
{
For (j, 0, i - 1) For (S, 0, Cm)
f[i][S] = (f[i][S] + 1ll * f[j][S] * eul[i - j][S] % MOD) % MOD;
FWT(n, f[i], -1);
For (S, 0, Cm)
if (sze[S] == i) f[i][S] = 1ll * f[i][S] * inv[S] % MOD;
else f[i][S] = 0;
FWT(n, f[i], 1);
}
FWT(n, f[n], -1);
cout << f[n][Cm] << endl;
return 0;
}