problem
洛谷链接
solution
显然题目指向:存在欧拉回路的州划分是不合法的。
当且仅当这个州是 联通 的且 内部没有奇数度数的点 时,这个州不合法。
因为 nnn 非常小,我们可以枚举每一种州划分方案,判断是否合法,顺便记录每个划分方案的贡献。
设 f(s):f(s):f(s): 当前划分集合为 sss 的答案,g(s):g(s):g(s): 划分一个州为 sss 是否合法,val(s):val(s):val(s): 划分州为 sss 的贡献。
列出状态转移方程:f(s)=∑t∈sf(s−t)g(t)(val(t)val(s))pf(s)=\sum_{t\in s}f(s-t)g(t)\Big(\frac{val(t)}{val(s)}\Big)^pf(s)=∑t∈sf(s−t)g(t)(val(s)val(t))p。
显然能看到有卷积的形式,即 f(s)=∑i⋂j=∅∧i⋃j=sf(j)g(i)(val(i)val(s))pf(s)=\sum_{i\bigcap j=\empty\wedge i\bigcup j=s}f(j)g(i)\Big(\frac{val(i)}{val(s)}\Big)^pf(s)=∑i⋂j=∅∧i⋃j=sf(j)g(i)(val(s)val(i))p。
i⋃j=si\bigcup j=si⋃j=s 可以直接用 FWT\text{FWT}FWT 做,但是 i⋂j=∅i\bigcap j=\emptyi⋂j=∅ 就不好做了。
如果结合并集为 sss 的信息,也就意味着 ∣i∣+∣j∣=∣s∣|i|+|j|=|s|∣i∣+∣j∣=∣s∣。
对于 dpdpdp 的设计可以再加一维二进制状态中 111 的个数。
f(i,s):sf(i,s):sf(i,s):s 中 111 的个数为 iii 的答案,g(i,s):[sg(i,s):[sg(i,s):[s 成立 ]∗val(s)]*val(s)]∗val(s)。
f(i,s)g(i,s)=∑j=0i−1∑t∈sf(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;
}