Description
Solution
首先我们可以轻松预处理出满足条件的点集。
设 f s f_s fs表示点集 s s s的点组成州的方案数的满意度之和, s u m s sum_s sums表示点集 s s s的人口的 p p p次方,当 s s s为合法州区时, g s = s u m s g_s=sum_s gs=sums,否则 g s = 0 g_s=0 gs=0。
那么,
f S = ∑ T ⊆ S , T ≠ ∅ g T × f S − T s u m S f_S=\sum_{T \subseteq S, T \neq \emptyset} \frac {g_T \times f_{S-T}} {sum_S} fS=T⊆S,T̸=∅∑sumSgT×fS−T
这个问题可以用集合卷积优化,时间复杂度 O ( 2 n n 2 ) O(2^nn^2) O(2nn2)
集合卷积
f S = ∑ A ∩ B = S , A ∪ B = ∅ g A × h B f_S=\sum_{A\cap B=S,A \cup B = \emptyset}g_A \times h_B fS=A∩B=S,A∪B=∅∑gA×hB
上式等价与 f S = ∑ A ∩ B = S , ∣ A ∣ + ∣ B ∣ = ∣ S ∣ g A × h B f_S=\sum_{A\cap B=S,|A|+|B|=|S|}g_A \times h_B fS=∑A∩B=S,∣A∣+∣B∣=∣S∣gA×hB
所以多设一维状态 f i , s f_{i,s} fi,s表示集合为 s s s,集合大小为 i i i。当 ∣ S ∣ ≠ i |S| \neq i ∣S∤=i时, f i , s f_{i,s} fi,s为 0 0 0
类似的,定义 h i , s , g i , s h_{i,s},g_{i,s} hi,s,gi,s。
所以有 f i , s = ∑ A ∩ B = S , j + k = i g j , A × h k , B f_{i,s}=\sum_{A\cap B=S,j+k=i}g_{j,A} \times h_{k,B} fi,s=∑A∩B=S,j+k=igj,A×hk,B。用 F W T FWT FWT解决即可。
#include <bits/stdc++.h>
using namespace std;
typedef long long lint;
const int maxn = 25, maxm = maxn * maxn / 2, mod = 998244353;
int n, m, p, U;
int u[maxm], v[maxm], w[maxn], deg[maxn];
int sum[1 << 21], g[maxn][1 << 21], f[maxn][1 << 21], bcnt[1 << 21], inv[2100 * 2100 + 5];
int fa[maxn];
inline int gi()
{
char c = getchar();
while (c < '0' || c > '9') c = getchar();
int sum = 0;
while ('0' <= c && c <= '9') sum = sum * 10 + c - 48, c = getchar();
return sum;
}
inline void inc(int &a, int b) {a += b; if (a >= mod) a -= mod;}
inline void dec(int &a, int b) {a -= b; if (a < 0) a += mod;}
inline int find(int x)
{
if (fa[x] == x) return x;
return fa[x] = find(fa[x]);
}
void FWT(int *a, int n)
{
for (int i = 1; i < n; i <<= 1)
for (int j = 0; j < n; j += (i << 1))
for (int k = 0; k < i; ++k) inc(a[i + j + k], a[j + k]);
}
void UFWT(int *a, int n)
{
for (int i = 1; i < n; i <<= 1)
for (int j = 0; j < n; j += (i << 1))
for (int k = 0; k < i; ++k) dec(a[i + j + k], a[j + k]);
}
int main()
{
n = gi(); m = gi(); p = gi();
for (int i = 1; i <= m; ++i) u[i] = gi(), v[i] = gi();
for (int i = 1; i <= n; ++i) w[i] = gi();
inv[1] = 1;
for (int i = 2; i <= 2100 * 2100; ++i) inv[i] = (lint)(mod - mod / i) * inv[mod % i] % mod;
for (int i = 1; i < (1 << n); ++i) bcnt[i] = bcnt[i ^ (i & (-i))] + 1;
for (int i = 1; i < (1 << n); ++i) {
for (int j = 1; j <= n; ++j) {
if ((i >> (j - 1)) & 1) sum[i] += w[j];
deg[j] = 0; fa[j] = j;
}
for (int j = 1; j <= m; ++j)
if (((i >> (u[j] - 1)) & 1) && ((i >> (v[j] - 1)) & 1)) ++deg[u[j]], ++deg[v[j]], fa[find(u[j])] = find(v[j]);
int flag = 0, root = 0;
for (int j = 1; j <= n; ++j)
if ((i >> (j - 1)) & 1) {
if (deg[j] & 1) {flag = 1; break;}
if (!root) root = find(j);
else if (find(j) != root) {flag = 1; break;}
}
if (p == 0) sum[i] = 1;
else if (p == 2) sum[i] = (lint)sum[i] * sum[i] % mod;
if (flag) g[bcnt[i]][i] = sum[i];
sum[i] = inv[sum[i]];
}
U = 1 << n;
for (int i = 0; i <= n; ++i) FWT(g[i], U);
f[0][0] = 1;
FWT(f[0], U);
for (int i = 1; i <= n; ++i) {
for (int j = 0; j < i; ++j)
for (int s = 0; s < U; ++s)
inc(f[i][s], (lint)f[j][s] * g[i - j][s] % mod);
UFWT(f[i], U);
for (int s = 0; s < U; ++s)
if (bcnt[s] != i) f[i][s] = 0;
else f[i][s] = (lint)f[i][s] * sum[s] % mod;
if (i != n) FWT(f[i], U);
}
printf("%d\n", f[n][U - 1]);
return 0;
}