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

【UOJ348】【WC2018】州区划分 状压DP FWT

时间:2018-03-06 17:05:49      阅读:140      评论:0      收藏:0      [点我收藏+]

标签:scan   多个   状态转移方程   pos   har   lib   post   class   const   

题目大意

  给定?个\(n\)个点的?向图,对于?种\(n\)个点的划分\(\{S_1,S_2,\ldots,S_k\}\),定义它是合法的,当且仅当每个点都在其中的一个集合中且对于任何的\(i\in[1,k]\),点集\(S_i\)?空,且导出?图不存在欧拉回路。

  给定数组\(w_i\),求对于所有合法的划分\(\{s_1,s_2,\ldots,s_k\}\),下面的式子之和
\[ {(\prod_{i=1}^k\frac{\sum_{x\in S_i}w_x}{\sum_{j=1}^i\sum_{x\in S_j}w_x})}^p \]
  \(n\leq 21\)

题解

  先用\(O(n^22^n)\)判断每个点集是否合法,并计算\(g(S)={(\sum_{x\in S}w_x)}^p\)。如果\(S\)不合法。那么\(g(S)=0\)

  很容易想到一个\(O(3^n)\)的做法。

  设\(f(S)\)为当前选择的集合为\(S\)的答案
\[ f(S)=\frac{\sum_{T\subseteq S}g(T)pf(S\setminus T)}{g(S)} \]
  可以发现这是一个子集卷积。

  一种可行的做法是把子集卷积转化为子集或卷积。

  定义\(\widetilde{f}\)\(f\)的集合占位幂级数,当且仅当对于所有的\(S\)满足\(\widetilde{f}(S)\)是一个\(|S|\)次多项式,且\([x^{|S|}]\widetilde{f}(S)=f(S)\)

  可以发现,若\(p(S)=\widetilde f(S)\cdot \widetilde g(S)\),则\(p\)\(f\times g\)的占位多项式。

  所以我们可以在\(O(n^22^n)\)内计算子集卷积了。

  回到这道题,我们假设每个城市可以出现在多个州里,记\(h_{i,S}\)为每个州的城市个数之和为\(i\),每个州的城市的并集为\(S\)的方案数。那么\(F(S)=\sum_{i=1}^nh_{i,S}x^i\)就是\(f(S)\)的集合占位幂级数。

  所以\(f(S)=h_{|S|,S}\),状态转移方程是
\[ h_{i,S}=\sum_{j=1}^i\sum_{|T|=j}\sum_{A}[A|T=S]h_{i-j,A}\frac{g(T)}{g(S)} \]
  记\(G(i)=\sum_{|S|=i}g(S)x^S\)

  我们先枚举\(i\),再枚举\(j\),然后计算\(h_i=h_{i-j}\times G(j)\)

  这里我们可以全程用莫比乌斯变换后的值,卷积一次就是\(O(2^n)\)

  还有,我们这里要除以\(g(S)\),可以在做完一层(\(h_i\))之后变换回去,除以\(g(S)\),再变换回来。

  这样时间复杂度就是\(O(n^22^n)\)的了。

代码

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<ctime>
#include<cstdlib>
#include<utility>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
void open(const char *s)
{
#ifndef ONLINE_JUDGE
    char str[100];
    sprintf(str,"%s.in",s);
    freopen(str,"r",stdin);
    sprintf(str,"%s.out",s);
    freopen(str,"w",stdout);
#endif
}
const ll p=998244353;
ll fp(ll a,ll b)
{
    ll s=1;
    for(;b;b>>=1,a=a*a%p)
        if(b&1)
            s=s*a%p;
    return s;
}
int lx[100010];
int ly[100010];
int n,m,o;
void fwt(int *a)
{
    int i,j;
    for(j=1;j<1<<n;j<<=1)
        for(i=0;i<1<<n;i++)
            if(i&j)
                a[i]=(a[i]+a[i^j])%p;
}
void ifwt(int *a)
{
    int i,j;
    for(j=1;j<1<<n;j<<=1)
        for(i=0;i<1<<n;i++)
            if(i&j)
                a[i]=(a[i]-a[i^j])%p;
}
int f[22][1<<21];
int g[22][1<<21];
ll inv[100010];
ll fw[1<<21];
ll fwi[1<<21];
int w[30];
int fa[100010];
int num[1<<21];
int find(int x)
{
    return fa[x]==x?x:fa[x]=find(fa[x]);
}
int c[1<<21];
int d[100010];
int main()
{
    open("walk");
    int i,j,k;
    inv[0]=inv[1]=1;
    for(i=2;i<=10000;i++)
        inv[i]=-p/i*inv[p%i]%p;
    scanf("%d%d%d",&n,&m,&o);
    for(i=1;i<=m;i++)
        scanf("%d%d",&lx[i],&ly[i]);
    for(i=1;i<=n;i++)
        scanf("%d",&w[i]);
    int all=(1<<n)-1;
    for(i=1;i<=all;i++)
    {
        for(j=1;j<=n;j++)
        {
            d[j]=0;
            fa[j]=j;
        }
        for(j=1;j<=m;j++)
            if(((i>>(lx[j]-1))&1)&&((i>>(ly[j]-1))&1))
            {
                d[lx[j]]^=1;
                d[ly[j]]^=1;
                int fx=find(lx[j]);
                int fy=find(ly[j]);
                if(fx!=fy)
                    fa[fx]=fy;
            }
        fw[i]=0;
        for(j=1;j<=n;j++)
            if((i>>(j-1))&1)
                fw[i]+=w[j];
        fwi[i]=inv[fw[i]];
        fw[i]=fp(fw[i],o);
        fwi[i]=fp(fwi[i],o);
        int cnt=0;
        c[i]=0;
        for(j=1;j<=n;j++)
            if((i>>(j-1))&1)
            {
                num[i]++;
                if(fa[j]==j)
                {
                    cnt++;
                    if(cnt>=2)
                        c[i]=1;
                }
                if(d[j])
                    c[i]=1;
            }
        fw[i]*=c[i];
        g[num[i]][i]=fw[i];
    }
    f[0][0]=1;
    for(i=1;i<=n;i++)
        fwt(g[i]);
    fwt(f[0]);
    for(i=1;i<=n;i++)
    {
        for(j=1;j<=i;j++)
            for(k=0;k<=all;k++)
                f[i][k]=(f[i][k]+(ll)f[i-j][k]*g[j][k])%p;
        ifwt(f[i]);
        for(j=0;j<=all;j++)
            f[i][j]=f[i][j]*fwi[j]%p;
        fwt(f[i]);
    }
    ifwt(f[n]);
    ll ans=f[n][all];
    ans=(ans+p)%p;
    printf("%lld\n",ans);
    return 0;
}

【UOJ348】【WC2018】州区划分 状压DP FWT

标签:scan   多个   状态转移方程   pos   har   lib   post   class   const   

原文地址:https://www.cnblogs.com/ywwyww/p/8514554.html

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