码迷,mamicode.com
首页 > 其他好文 > 详细

[WC2018]州区划分

时间:2018-12-27 18:54:25      阅读:168      评论:0      收藏:0      [点我收藏+]

标签:ace   ++i   联通   getchar   struct   spl   交集   find   dig   

题意

题面

给一张带点权的无向图

要求对其划分为联通且不存在欧拉回路的多个子图

定义一个子图的贡献是

\(i\)个子图的点权和占前\(i\)个子图的点权和的比例的\(p\)次幂

定义一个划分的贡献是

该划分下所有子图的贡献的乘积

求所有划分的贡献之和

题解

\(f_S\)为选取点集为\(S\)时所有划分的贡献和


\[ f_S=\frac{\sum_{T \in S}{f_{S-T}*Sum_T^p}}{Sum_S^p} \]
注意要求\(T\)合法

可以枚举预处理

只需保证联通(并查集)

且没有欧拉回路(所有点度数为偶数)

至此复杂度\(O(3^n)\)

考虑优化

分子部分显然是集合卷积的形式

但与普通的或卷积不同

这个式子要求两个集合交集为空才能转移

这里有一个大智若愚、苦尽甘来、有舍有得、其实就是我太菜了的做法

加一维状态\(i\)表示所选点集的大小

这样复杂度就乘上一个$n^2 $啦

就可以直接或卷积转移

具体说就是

枚举点集\(S\)\(S-T\)大小

再直接FWTFMT转移

这样如果两个集合有交集贡献将是\(0\)

复杂度\(O(n^22^n)\)

#include<bits/stdc++.h>

using namespace std;

#define gc c=getchar()
#define r(x) read(x)
#define ll long long 

template<typename T>
inline void read(T&x){
    x=0;T k=1;char gc;
    while(!isdigit(c)){if(c==‘-‘)k=-1;gc;}
    while(isdigit(c)){x=x*10+c-‘0‘;gc;}x*=k;
}

const int N=25;
const int S=1<<21|7;
const int p=998244353;

struct Edge{
    int u,v;
}E[N*N];

int fa[N];

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

inline void uni(int u,int v){
    u=find(u),v=find(v);
    if(u!=v)fa[u]=v;
}

inline bool in(int S,int x){
    return S>>(x-1)&1;
}

int n,m,q;

int deg[N];

inline bool check(int S){
    for(int i=1;i<=n;++i){
        fa[i]=i;
        deg[i]=0;
    }
    for(int i=1;i<=m;++i){
        int u=E[i].u,v=E[i].v;
        if(in(S,u)&&in(S,v)){
            uni(u,v);
            ++deg[u];
            ++deg[v];
        }
    }
    int lst=0;
    for(int i=S;i;i^=i&(-i)){
        int x=__builtin_ffs(i);
        if(deg[x]&1)return 1;
        if(lst&&find(x)!=lst)return 1;
        lst=find(x);
    }
    return 0;
}

inline void add(int &a,int b){
    a+=b;
    if(a>=p)a-=p;
}

inline void sub(int &a,int b){
    a-=b;
    if(a<0)a+=p;
}

inline ll qpow(ll a,ll b){
    ll ans=1;
    while(b){
        if(b&1)(ans*=a)%=p;
        (a*=a)%=p;
        b>>=1;
    }
    return ans;
}

inline void fmt(int *A){
    for(int i=0;i<n;++i){
        for(int j=0;j<1<<n;++j){
            if(j>>i&1)add(A[j],A[j^(1<<i)]);
        }
    }
}

inline void ifmt(int *A){
    for(int i=0;i<n;++i){
        for(int j=0;j<1<<n;++j){
            if(j>>i&1)sub(A[j],A[j^(1<<i)]);
        }
    }
}

int w[N];
int sum[S];
int Inv[S];
int bit[S];
int g[N][S];
int f[N][S];

int main(){
    r(n),r(m),r(q);
    for(int i=1;i<=m;++i){
        int u,v;r(u),r(v);
        E[i]=Edge{u,v};
    }
    for(int i=1;i<=n;++i){
        r(w[i]);
    }
    int S=(1<<n)-1;
    for(int i=1;i<=S;++i){
        sum[i]=w[__builtin_ffs(i)]+sum[i^(i&(-i))];
        bit[i]=bit[i>>1]+(i&1);
        int tmp=qpow(sum[i],q);
        g[bit[i]][i]=check(i)*tmp;
        Inv[i]=qpow(tmp,p-2);
    }
    for(int i=1;i<=n;++i)fmt(g[i]);
    f[0][0]=1;fmt(f[0]);
    for(int i=1;i<=n;++i){
        for(int j=0;j<i;++j){
            for(int k=0;k<1<<n;++k){
                add(f[i][k],(ll)f[j][k]*g[i-j][k]%p);
            }
        }
        ifmt(f[i]);
        for(int k=0;k<1<<n;++k)f[i][k]=i==bit[k]?(ll)f[i][k]*Inv[k]%p:0;
        if(i^n)fmt(f[i]);
    }
    printf("%d\n",f[n][S]);
}

[WC2018]州区划分

标签:ace   ++i   联通   getchar   struct   spl   交集   find   dig   

原文地址:https://www.cnblogs.com/yicongli/p/10185234.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!