[WC2018]州区划分 - FWT - 状压dp

……直接记bitcount然后FWT即可。

// luogu-judger-enable-o2
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;typedef long long lint;
inline int inn() { int x;scanf("%d",&x);return x; }
const int N=22,MXS=(1<<21)+5,p=998244353;int sz[MXS],lb[MXS],id[MXS],Sum[MXS],dp[N][MXS],ok[N][MXS],g[N][N],w[N];
namespace GETOK_space{
    static int a[N],fa[N],d[N];inline int findf(int x) { return x==fa[x]?x:fa[x]=findf(fa[x]); }
    inline int getok(int s,int n)
    {
        int cnt=0,x,t=0,u,v;
        rep(i,0,n-1) if((s>>i)&1) a[++cnt]=i,d[cnt]=0,fa[cnt]=cnt;
        rep(i,1,cnt) rep(j,i+1,cnt) x=g[a[i]][a[j]],t+=x,d[i]^=x,d[j]^=x,
            (x?u=findf(i),v=findf(j),(u!=v?fa[u]=v:0):0);
        rep(i,1,cnt) if(findf(i)^findf(1)) return Sum[s];
        rep(i,1,cnt) if(d[i]&1) return Sum[s];return 0;
    }
}using GETOK_space::getok;const int MXW=N*100;int inv[MXW],qwq[MXS];
inline int fwt(int *a,int n)
{ for(int i=1;i<n;i<<=1) for(int j=0,t=i<<1;j<n;j+=t) rep(k,0,i-1) a[j+k+i]+=a[j+k],(a[j+k+i]>=p?a[j+k+i]-=p:0);return 0; }
inline int ufwt(int *a,int n)
{ for(int i=1;i<n;i<<=1) for(int j=0,t=i<<1;j<n;j+=t) rep(k,0,i-1) a[j+k+i]-=a[j+k],(a[j+k+i]<0?a[j+k+i]+=p:0);return 0; }
inline int fast_pow(int x,int k,int ans=1) { for(;k;k>>=1,x=(lint)x*x%p) (k&1)?ans=(lint)ans*x%p:0;return ans; }
int main()
{
    int n=inn(),m=inn(),k=inn(),x,y,all=(1<<n)-1;
    rep(i,1,m) x=inn()-1,y=inn()-1,g[x][y]++,g[y][x]++;
    rep(i,0,n-1) w[i]=inn();rep(i,1,MXW-1) inv[i]=fast_pow(i,p-2);
    rep(i,0,n-1) id[1<<i]=i;rep(i,1,all) lb[i]=i&-i;
    rep(i,1,all) sz[i]=sz[i^lb[i]]+1,Sum[i]=Sum[i^lb[i]]+w[id[lb[i]]];
    rep(i,0,all) qwq[i]=inv[Sum[i]],qwq[i]=(k==0?1:(k==1?qwq[i]:(lint)qwq[i]*qwq[i]%p));
    rep(i,0,all) Sum[i]=(k==0?1:(k==1?Sum[i]:Sum[i]*Sum[i]));
    rep(i,1,all) ok[sz[i]][i]=getok(i,n);
    rep(i,1,n) fwt(ok[i],all);dp[0][0]=1,fwt(dp[0],all);
    rep(i,1,n)
    {
        rep(j,1,i) rep(k,0,all) dp[i][k]=(dp[i][k]+(lint)dp[i-j][k]*ok[j][k])%p;
        ufwt(dp[i],all);rep(k,0,all) dp[i][k]=(lint)dp[i][k]*qwq[k]%p;(i<n?fwt(dp[i],all):0);
    }
    return !printf("%d\n",dp[n][all]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值