P4221 [WC2018] 州区划分
半在线子集卷积例题。(有点卡常)
首先判断一个点的集合是否为合法洲,这个可以先枚举点集再根据出入度和连通性判断欧拉回路,复杂度 \(O(2^nn^2)\)。
设 \(W(s)\) 表示权值总和。
对于合法洲,其贡献函数 \(G(s)=W(s)\) ,否则为 \(G(s)=0\)。设 \(F(s)\) 表示点集 \(s\) 构成的洲区分化的所有合法方案的满意度之和。
有 \(F(s)=\sum\limits_{i \& j=0,i|j=S,j>0}F(i)(\frac{G(j)}{W(s)})^P\)。这是一个半在线子集卷积。初始化 \(F(0)=0\),答案为 \(F(\)满集\()\)。
考虑普通的子卷积,把 \(1\) 的个数 \(x\) 提到前面,按 \(x\) 从小到大做。
code
#include<bits/stdc++.h>
typedef long long LL;
using namespace std;
const int N=(1<<21)+5,Mod=998244353;
int n,m,P,S,Eu[225],Ev[225],ind[23],w[23],cz[N],fa[23],vf[23];
LL W[N],g[N],F[22][N],G[22][N];
int find(int x){
if(fa[x]==x) return x;
else return fa[x]=find(fa[x]);
}
LL Pow(LL x,LL k){
LL y=1;
while(k){
if(k & 1) y=y*x%Mod;
k>>=1; x=x*x%Mod;
}
return y;
}
void FWT(LL *f,int n,int ff){
for(int p=2;p<=n;p<<=1){
int pn=p>>1;
for(int i=0;i<n;i+=p)
for(int j=i;j<i+pn;j++)
f[j+pn]=(f[j+pn]+f[j]*ff+Mod)%Mod;
}
}
int main(){
scanf("%d%d%d",&n,&m,&P); S=1<<n;
for(int i=1;i<S;i++) cz[i]=cz[i>>1]+(i & 1);
for(int i=1;i<=m;i++) {
scanf("%d%d",&Eu[i],&Ev[i]);
Eu[i]--; Ev[i]--;
}
for(int i=0;i<n;i++) scanf("%d",&w[i]);
for(int i=1;i<S;i++){
for(int j=0;j<n;j++) {
ind[j]=0; fa[j]=j; vf[j]=0;
if((i>>j) & 1) W[i]+=w[j];
}
W[i]=Pow(W[i],P);
for(int j=1;j<=m;j++)
if(((i>>Eu[j]) & 1) && ((i>>Ev[j]) & 1)) {
ind[Eu[j]]++,ind[Ev[j]]++;
fa[find(Eu[j])]=find(Ev[j]);
}
int c=0,r=0;
for(int j=0;j<n;j++)
if(((i>>j) & 1) && !vf[find(j)]) vf[fa[j]]=1,c++;
for(int j=0;j<n;j++) if(ind[j]%2) {r=1; break;}
if(r || c>1) g[i]=W[i];
W[i]=Pow(W[i],Mod-2);
}
for(int i=0;i<S;i++) G[cz[i]][i]=g[i];
for(int i=0;i<=n;i++) FWT(G[i],S,1);
F[0][0]=1; FWT(F[0],S,1);
for(int i=1;i<=n;i++){
for(int j=0;j<i;j++)
for(int k=0;k<S;k++) (F[i][k]+=F[j][k]*G[i-j][k])%=Mod;
FWT(F[i],S,-1);
for(int j=0;j<S;j++)
if(cz[j]==i) (F[i][j]*=W[j])%=Mod;
else F[i][j]=0;
if(i!=n) FWT(F[i],S,1);
}
printf("%lld",F[n][S-1]);
return 0;
}

浙公网安备 33010602011771号