LOJ2340 [WC2018] 州区划分 【FMT】【欧拉回路】

时间:2021-07-26 05:49:08

题目分析:

这题是WC的题???

$g[S] = (\sum_{x \in S}w_x)^p$

$h[S] = g[S]$如果$S$不是欧拉回路

$d[S] = \frac{f[S]}{g[All-S]^p}$

$f[S] = \sum_{T \subset S}d[S-T]*h[T]$

总数等于$f[All]$

代码:

 #include<bits/stdc++.h>
using namespace std; const int maxn = ;
const int mod = ; int n,m,p,f[maxn][(<<)+],g[(<<)+],d[maxn][(<<)+],ng[maxn][maxn];
int iv[(<<)+],popcnt[(<<)+],w[maxn]; int fast_pow(int now,int pw){
int ans = ,dt = now,bit = ;
while(bit <= pw){
if(bit & pw){ans = 1ll*ans*dt%mod;}
dt = 1ll*dt*dt%mod; bit<<=;
}
return ans;
} void read(){
scanf("%d%d%d",&n,&m,&p);
for(int i=;i<(<<n);i++) popcnt[i] = popcnt[i>>]+(i&);
for(int i=;i<=m;i++){
int u,v; scanf("%d%d",&u,&v);
ng[u][v] = ng[v][u] = ;
}
for(int i=;i<=n;i++) scanf("%d",&w[i]);
} void dp(int now){
if(g[now] || now == ) return;
dp(now-(now&-now));
g[now] = g[now-(now&-now)];
for(int i=;i<=n;i++) if((<<i-)&(now&-now)) g[now] += w[i];
} queue<int> q;int arr[maxn];
int chk(int now){
for(int i=;i<n;i++) if(now&(<<i)){arr[i+]=now;q.push(i+);break;}
while(!q.empty()){
int k = q.front();q.pop();
for(int i=;i<=n;i++){
if(!(now&(<<i-)) || !ng[k][i] || arr[i]==now) continue;
arr[i] = now; q.push(i);
}
}
for(int i=;i<=n;i++) if(now&(<<i-)) if(arr[i]!=now) return ;
for(int i=;i<=n;i++){
if(!(now&(<<i-))) continue;
int cnt = ;
for(int j=;j<=n;j++){if((now&(<<j-))&&ng[i][j]) cnt++;}
if(cnt & ) return ;
}
return ;
} void FMT(int *A){
for(int i=;i<(<<n);i<<=)
for(int j=;j<(<<n);j+=(i<<))
for(int k=;k<i;k++) {
A[j+k+i] += A[j+k];
if(A[j+k+i] >= mod) A[j+k+i] -= mod;
}
} void IFMT(int *A){
for(int i=(<<n-);i>=;i>>=)
for(int j=;j<(<<n);j+=(i<<))
for(int k=;k<i;k++){
A[j+k+i] -= A[j+k];
if(A[j+k+i] < ) A[j+k+i] += mod;
}
} void init(){
for(int i=;i<(<<n);i++) dp(i);
for(int i=;i<(<<n);i++){if(p==)g[i]=;else if(p==)g[i]=g[i]*g[i];}
for(int i=;i<(<<n);i++) iv[i] = fast_pow(g[i],mod-);
for(int i=;i<(<<n);i++){
if(chk(i)) continue; // if it's Euler Graph
int z = ,p = i; while(p) {if(p&)z++;p>>=;}
d[z][i] = g[i];
}
for(int i=;i<=n;i++)
FMT(d[i]);
for(int i=;i<(<<n);i++) f[][i] = iv[(<<n)-];
} void work(){
for(int i=;i<=n;i++){
for(int j=;j<i;j++){
for(int k=;k<(<<n);k++){
f[i][k] += 1ll*f[j][k]*d[i-j][k]%mod;
if(f[i][k] >= mod) f[i][k] -= mod;
}
}
IFMT(f[i]);
if(i < n){
for(int j=;j<(<<n);j++){
if(popcnt[j] != i) f[i][j] = ;
else{f[i][j] = 1ll*f[i][j]*iv[(<<n)-j-]%mod;}
}
FMT(f[i]);
}
}
printf("%d\n",f[n][(<<n)-]);
} int main(){
read();
init();
work();
return ;
}