problem
solution
显然题目指向:存在欧拉回路的州划分是不合法的。
当且仅当这个州是 联通 的且 内部没有奇数度数的点 时,这个州不合法。
因为 n n n 非常小,我们可以枚举每一种州划分方案,判断是否合法,顺便记录每个划分方案的贡献。
设 f ( s ) : f(s): f(s): 当前划分集合为 s s s 的答案, g ( s ) : g(s): g(s): 划分一个州为 s s s 是否合法, v a l ( s ) : val(s): val(s): 划分州为 s s s 的贡献。
列出状态转移方程: f ( s ) = ∑ t ∈ s f ( s − t ) g ( t ) ( v a l ( t ) v a l ( s ) ) p f(s)=\sum_{t\in s}f(s-t)g(t)\Big(\frac{val(t)}{val(s)}\Big)^p f(s)=∑t∈sf(s−t)g(t)(val(s)val(t))p。
显然能看到有卷积的形式,即 f ( s ) = ∑ i ⋂ j = ∅ ∧ i ⋃ j = s f ( j ) g ( i ) ( v a l ( i ) v a l ( s ) ) p f(s)=\sum_{i\bigcap j=\empty\wedge i\bigcup j=s}f(j)g(i)\Big(\frac{val(i)}{val(s)}\Big)^p f(s)=∑i⋂j=∅∧i⋃j=sf(j)g(i)(val(s)val(i))p。
i ⋃ j = s i\bigcup j=s i⋃j=s 可以直接用 FWT \text{FWT} FWT 做,但是 i ⋂ j = ∅ i\bigcap j=\empty i⋂j=∅ 就不好做了。
如果结合并集为 s s s 的信息,也就意味着 ∣ i ∣ + ∣ j ∣ = ∣ s ∣ |i|+|j|=|s| ∣i∣+∣j∣=∣s∣。
对于 d p dp dp 的设计可以再加一维二进制状态中 1 1 1 的个数。
f ( i , s ) : s f(i,s):s f(i,s):s 中 1 1 1 的个数为 i i i 的答案, g ( i , s ) : [ s g(i,s):[s g(i,s):[s 成立 ] ∗ v a l ( s ) ]*val(s) ]∗val(s)。
f ( i , s ) g ( i , s ) = ∑ j = 0 i − 1 ∑ t ∈ s f ( j , s − t ) g ( i − j , t ) f(i,s)g(i,s)=\sum_{j=0}^{i-1}\sum_{t\in s}f(j,s-t)g(i-j,t) f(i,s)g(i,s)=∑j=0i−1∑t∈sf(j,s−t)g(i−j,t)。
预处理贡献和贡献的逆元,状态是否合法,直接 FWT \text{FWT} FWT 卷积即可。
code
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define mod 998244353
int n, m, p, N;
int u[250], v[250], w[25], fa[25], d[25], val[1 << 21], tot[1 << 21], inv[1 << 21];
bool ok[1 << 21];
int f[22][1 << 21], g[22][1 << 21];
int qkpow( int x, int y ) {
int ans = 1;
while( y ) {
if( y & 1 ) ans = ans * x % mod;
x = x * x % mod;
y >>= 1;
}
return ans;
}
void fwt( int *c, int f ) {
for( int i = 1;i < N;i <<= 1 )
for( int j = 0;j < N;j += ( i << 1 ) )
for( int k = 0;k < i;k ++ )
c[j + k + i] = ( c[j + k + i] + c[j + k] * f % mod + mod ) % mod;
}
int find( int x ) { return x == fa[x] ? x : fa[x] = find( fa[x] ); }
signed main() {
scanf( "%lld %lld %lld", &n, &m, &p );
for( int i = 1;i <= m;i ++ ) scanf( "%lld %lld", &u[i], &v[i] );
for( int i = 1;i <= n;i ++ ) scanf( "%lld", &w[i] );
N = 1 << n;
for( int s = 0;s < N;s ++ ) {
int num = 0;
for( int i = 1;i <= n;i ++ ) {
if( 1 << i - 1 & s ) num ++, val[s] += w[i];
d[i] = 0, fa[i] = i;
}
tot[s] = num;
for( int i = 1;i <= m;i ++ )
if( 1 << u[i] - 1 & s and 1 << v[i] - 1 & s ) {
int fu = find( u[i] ), fv = find( v[i] );
if( fu ^ fv ) num --, fa[fv] = fu;
d[u[i]] ++, d[v[i]] ++;
}
if( num ^ 1 ) ok[s] = 1;
num = 0;
for( int i = 1;i <= n;i ++ ) num += d[i] & 1;
if( num ) ok[s] = 1;
if( ok[s] ) g[tot[s]][s] = qkpow( val[s], p );
inv[s] = qkpow( qkpow( val[s], p ), mod - 2 );
}
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;j ++ )
for( int k = 0;k < N;k ++ )
f[i][k] = ( f[i][k] + f[j][k] * g[i - j][k] ) % mod;
fwt( f[i], -1 );
for( int j = 0;j < N;j ++ ) f[i][j] = tot[j] == i ? f[i][j] * inv[j] % mod : 0;
fwt( f[i], 1 );
}
printf( "%lld\n", f[n][N - 1] );
return 0;
}