博主的 BiBi 时间

感觉学到了几个卡常小技巧,这题卡空间又卡时间可还行

另外代码在 \(\sf{usOJ}\) 上只有 \(\text{50 pts}\)(其他都可过)。

Solution

首先,不合法的州就是内部有欧拉回路的州。因此,只要连通且没有奇数度的点,这个州就不合法。

还是 "权值为乘积,求权值和" 的形式,于是可以想到枚举加入的州。记 \(f(S)\) 为总集合为 \(S\) 时的答案,\(g(S)\)\(S\) 集合中 \(w\) 值之和的 \(p\) 次方。那么有(枚举 \(S\setminus S'\)

\[f(S)=\frac{1}{g(S)}\cdot \sum_{S'\subsetneq S}f(S')g(S\setminus S') \]

这就是 子集卷积,唯一不同的是这是 \(f\) 卷上 \(g\) 再得到 \(f\),但是这也不影响。所以我们套路地加上一维表示集合中 \(1\) 的个数。复杂度还是 \(\mathcal O(n^22^n)\) 的。

Code

注意 \(f\) 数组的清空。

#include <cstdio>

#define rep(i,_l,_r) for(register signed i=(_l),_end=(_r);i<=_end;++i)
#define fep(i,_l,_r) for(register signed i=(_l),_end=(_r);i>=_end;--i)
#define erep(i,u) for(signed i=head[u],v=to[i];i;i=nxt[i],v=to[i])
#define efep(i,u) for(signed i=Head[u],v=to[i];i;i=nxt[i],v=to[i])
#define print(x,y) write(x),putchar(y)

template <class T> inline T read(const T sample) {
    T x=0; int f=1; char s;
    while((s=getchar())>'9'||s<'0') if(s=='-') f=-1;
    while(s>='0'&&s<='9') x=(x<<1)+(x<<3)+(s^48),s=getchar();
    return x*f;
}
template <class T> inline void write(const T x) {
    if(x<0) return (void) (putchar('-'),write(-x));
    if(x>9) write(x/10);
    putchar(x%10^48);
}
template <class T> inline T Max(const T x,const T y) {if(x>y) return x; return y;}
template <class T> inline T Min(const T x,const T y) {if(x<y) return x; return y;}
template <class T> inline T fab(const T x) {return x>0?x:-x;}
template <class T> inline T gcd(const T x,const T y) {return y?gcd(y,x%y):x;}
template <class T> inline T lcm(const T x,const T y) {return x/gcd(x,y)*y;}
template <class T> inline T Swap(T &x,T &y) {x^=y^=x^=y;}

const int mod=998244353;

int n,m,p,u[400],v[400],w[25],lim,fa[25],deg[25],cnt,val[(1<<21)+5],f[25][(1<<21)+5],g[25][(1<<21)+5],bit[(1<<21)+5],inv[(1<<21)+5];
bool ok[(1<<21)+5];

inline void inc(int &x,const int y) {
    x=x+y;
    if(x>=mod) x=x-mod;
    if(x<0) x=x+mod;
}

inline int mul(const int x,const int y) {return 1ll*x*y-1ll*x*y/mod*mod;}

int Find(int x) {return x==fa[x]?x:fa[x]=Find(fa[x]);}

int qkpow(int x,int y) {
    int r=1;
    while(y) {
        if(y&1) r=mul(r,x);
        x=mul(x,x); y>>=1;
    }
    return r;
}

void FWT_or(int *t,const int op=1) {
    for(int i=2;i<=lim;i<<=1)
        for(int p=(i>>1),j=0;j<lim;j=j+i)
            for(int k=j;k<j+p;++k)
                inc(t[k+p],t[k]*op);
}

int main() {
    int x,y;
    n=read(9),m=read(9),p=read(9); lim=1<<n;
    rep(i,1,m) u[i]=read(9),v[i]=read(9);
    rep(i,1,n) w[i]=read(9);
    rep(S,0,lim-1) {
        cnt=0;
        rep(i,0,n-1) {
            if((S>>i)&1) ++cnt,val[S]+=w[i+1];
            deg[i+1]=0,fa[i+1]=i+1;
        }
        bit[S]=cnt;
        rep(i,1,m)
            if(((S>>u[i]-1)&1)&&((S>>v[i]-1)&1)) {
                x=Find(u[i]),y=Find(v[i]);
                if(x^y) fa[x]=y,--cnt;
                ++deg[u[i]],++deg[v[i]];
            }
        if(cnt>1) ok[S]=1;
        cnt=0;
        rep(i,1,n) cnt+=(deg[i]&1);
        if(cnt) ok[S]=1;
        if(ok[S])
            g[bit[S]][S]=qkpow(val[S],p);
        inv[S]=qkpow(qkpow(val[S],mod-2),p);
        // 注意不是在 S 是合法州时才算 inv,因为 f[S] 可以表示几个州合在一起的方案
    }
    f[0][0]=1;
    FWT_or(f[0]);
    rep(i,0,n) FWT_or(g[i]);
    rep(i,1,n) {
        rep(j,0,i-1)
            rep(k,0,lim-1)
                inc(f[i][k],mul(f[j][k],g[i-j][k]));
        FWT_or(f[i],-1);
        rep(j,0,lim-1) // 因为 f[i] 会在之后做出贡献,需要将不合法的清空,记得除以 g[j]
            if(bit[j]^i) f[i][j]=0;
            else f[i][j]=mul(f[i][j],inv[j]);
        if(i^n) FWT_or(f[i]);
    }
    print(f[n][lim-1],'\n');
    return 0;
}
posted on 2020-06-12 22:02  Oxide  阅读(214)  评论(0编辑  收藏  举报