luogu什么情况= =
me就交了三次,还都被卡常了
再交了一遍就直接全部返回RE了?合着以为me是在卡评测吗……?
然后就去UOJ上过了这题……
luogu差评*1
UOJ#348传送门
看题可戳传送门
之前去WC的时候还不会FWT(或者FMT,反正都不会),于是考场上只写了 3n 3 n 做法
(还记得当时,怎么都过不了样例的绝望hhhh)
嗯!然后现在把这个坑给填了
首先根据题意,可以写出一个很显然的dp,就是: dp[k][s]=∑i∑j[i+j→s] dp[k−1][i]∗(sum[j]sum[s])p d p [ k ] [ s ] = ∑ i ∑ j [ i + j → s ] d p [ k − 1 ] [ i ] ∗ ( s u m [ j ] s u m [ s ] ) p 。 dp[k][s] d p [ k ] [ s ] 表示分了k个州,选择的城市集合为s时,满意度乘积的和。 i+j→s i + j → s 的意思是, i i 与 j j 无交,且 i i 并 j j 是 s s 。其中需要保证 j j 是一个合法的划分,这个可以预处理
可以发现,第一维并没有什么卵用。因为最终我们想要的,不是分组怎么样,而是只要分组合理就可以了。所以可以把第一维省掉
然后就变成了这样: dp[s]=∑i∑j[i+j→s] dp[i]∗(sum[j]sum[s])p d p [ s ] = ∑ i ∑ j [ i + j → s ] d p [ i ] ∗ ( s u m [ j ] s u m [ s ] ) p
化简一下变成了这样: sum[s]p∗dp[s]=∑i∑j[i+j→s] dp[i]∗sum[j]p s u m [ s ] p ∗ d p [ s ] = ∑ i ∑ j [ i + j → s ] d p [ i ] ∗ s u m [ j ] p
发现这已经是一个很明显的子集卷积的形式,于是把数组按二进制1的个数拆开,然后做FMT即可
这时候,如果你觉得,这是一个自己和自己卷积的形式,因此无法做拆位FMT的话……那么恭喜你!你和me一样蠢……QAQ
因为这个dp式子,虽然看起来是自己和自己卷,然而如果把拆位的那一维也写出来,就可以发现,其实已经形成了普通的子集卷积形式,因此是可以直接按照顺序求的
#include
#include
#include
#include
using namespace std ;
const int P = 998244353 ;
int N , M , px , sumw[1<<21] ;
int Fstate , acce[25] , sum[22][1<<21] , dp[22][1<<21] ;
short popcnt[1<<21] ;
map<int,int> bi ;
long long s_pow( long long x , int b ){
long long rt = 1 ;
while( b ){
if( b&1 ) rt = rt * x %P ;
x = x * x %P , b >>= 1 ;
} return rt ;
}
int vis ;
void dfs( int S , int u ){
vis |= ( 1 << u ) ;
for( int v = acce[u] ; v ; v -= v&-v )
if( ( S & (v&-v) ) && !( vis & (v&-v) ) ) dfs( S , bi[v&-v] ) ;
}
void check( int S ){
vis = 0 ;
int ok = 0 ;
for( int i = 0 ; i < N ; i ++ ){
if( !( S & ( 1 << i ) ) ) continue ;
if( popcnt[ S&acce[i] ] & 1 ){ ok = 1 ; break ; }
} if( !ok ) dfs( S , bi[S&-S] ) ;
if( ok || popcnt[vis] != popcnt[S] ){
if( px == 0 ) sum[popcnt[S]][S] = 1 ;
else if( px == 1 ) sum[popcnt[S]][S] = sumw[S] ;
else sum[popcnt[S]][S] = sumw[S] * sumw[S] ;
}
}
void preWork(){
Fstate = ( 1 << N ) - 1 ;
for( int i = 0 ; i <= N ; i ++ ) bi[1<for( int i = 1 ; i <= Fstate ; i ++ ){
popcnt[i] = popcnt[ i^(i&-i) ] + 1 ;
sumw[i] = sumw[ i^(i&-i) ] + sumw[i&-i] ;
} for( int i = 1 ; i <= Fstate ; i ++ ) check( i ) ;
}
void FMT( int *a ){
for( int i = 0 ; i < N ; i ++ )
for( int j = 0 ; j <= Fstate ; j ++ )
if( j&(1<1<void IFMT( int *a ){
for( int i = 0 ; i < N ; i ++ )
for( int j = 0 ; j <= Fstate ; j ++ )
if( j&(1<1<void solve(){
for( int i = 1 ; i <= N ; i ++ ) FMT( sum[i] ) ;
for( int i = 0 ; i <= Fstate ; i ++ ) dp[0][i] = 1 ;
for( int i = 1 ; i <= N ; i ++ ){
int *g = dp[i] ;
for( int j = 0 ; j < i ; j ++ ){
int *f = dp[j] , *q = sum[i-j] ;
for( int r = 0 ; r <= Fstate ; r ++ )
g[r] = ( g[r] + 1LL * f[r] * q[r] )%P ;
} IFMT( g ) ;
if( px ) for( int r = 0 ; r <= Fstate ; r ++ ){
if( popcnt[r] != i ) continue ;
long long tmp = s_pow( sumw[r] , P - 2 ) ;
if( px == 2 ) tmp = tmp * tmp %P ;
g[r] = tmp * g[r] %P ;
} if( i != N ) FMT( g ) ;
} printf( "%d" , ( dp[N][Fstate] + P )%P ) ;
}
int main(){
scanf( "%d%d%d" , &N , &M , &px ) ;
for( int i = 1 , u , v ; i <= M ; i ++ ){
scanf( "%d%d" , &u , &v ) ;
u -- , v -- ;
acce[u] |= ( 1 << v ) ;
acce[v] |= ( 1 << u ) ;
} for( int i = 0 ; i < N ; i ++ )
scanf( "%d" , &sumw[1<