• [WC2018]州区划分——FWT+DP+FST


    题目链接:

    [WC2018]州区划分

    题目大意:给n个点的一个无向图,点有点权,要求将这n个点划分成若干个部分,每部分合法当且仅当这部分中所有点之间的边不能构成欧拉回路。对于一种划分方案,第i个部分的权值为这一部分中所有点的权值和比上前i部分所有点的权值和的p次方,一种划分方案的权值为每部分的权值之积。要求求出所有划分方案的权值之和。

    我们设f[S]为选中点的状态集合为S时的答案(其中S为二进制状态),设T为S集合最后一次划分出的集合且要保证集合T合法,那么可以得到转移方程(其中sum代表集合中点权和):

    $f[S]=sumlimits_{Tsubseteq S}^{ }f[S-T]*(frac{sum[T]}{sum[S]})^p$

    这个子集DP直接枚举子集的时间复杂度是O(3^n),显然过不去,但我们发现这个DP相当于枚举两个集合i,j满足$icap j= varnothing  ,icup j=S$

    这个如果只有子集并的条件可以用直接用FWT来优化,但还要求交集为空的条件就不能一维DP优化了。

    我们假设一个点能被划分到多个部分中,那么DP状态就变成了二维:f[i][S]表示选取点集合为S,每部分包含的点数和为i的答案。

    设g[i][S]表示集合为S,选取点数为i时sum[S]的p次方,如果S不合法或|S|!=i,那么g[i][S]就为0。(其中|S|表示S集合中的点数即二进制状态中1的个数)

    那么转移方程就变成了:

    $f[i][S]=sumlimits_{j=1}^{i}sumlimits_{Tsubseteq S}^{ }frac{f[j][S-T]*g[i-j][T]}{sum[S]^p}$

    这样我们对f数组和g数组进行FWT转化成子集和表达式然后DP,每次乘上sum[S]^p的逆元即可,最后的答案为f[|U|][U],其中U为全集。时间复杂度为O(2^n*n^2)。

    再来说一下如何判欧拉回路:

    这个很简单只要一个图联通且每个点的度数都是偶数,那么这个图就是欧拉回路,对于每个二进制状态预处理判断即可,用bfs或dfs或并查集判断都可以。预处理时间复杂度同样是O(2^n*n^2)。

    #include<set>
    #include<map>
    #include<queue>
    #include<cmath>
    #include<stack>
    #include<cstdio>
    #include<vector>
    #include<bitset>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    int f[22][2100000];
    int g[22][2100000];
    int n,m,p;
    const int mod=998244353;
    int x[10000];
    int y[10000];
    int s[30];
    int fa[30];
    int v[22];
    int w[2100000];
    int cnt;
    int mask;
    int lg[2100000];
    int inv[2100000];
    inline int find(int x)
    {
        if(fa[x]==x)
        {
            return x;
        }
        return fa[x]=find(fa[x]);
    }
    inline void FWT(int *f,int opt)
    {
        for(int k=2;k<=(1<<n);k<<=1)
        {
            for(int i=0,t=k>>1;i<(1<<n);i+=k)
            {
                for(int j=i;j<i+t;j++)
                {
                    if(opt==1)
                    {
                    	f[j+t]=(f[j+t]+f[j])%mod;
                    }
                    else
                    { 
                    	f[j+t]=(f[j+t]-f[j]+mod)%mod;
                    }
                }
            }
        }
    }
    inline int quick_pow(int x,int y)
    {
        int res=1;
        while(y)
        {
            if(y&1)
            {
                res=1ll*res*x%mod;
            }
            y>>=1;
            x=1ll*x*x%mod;
        }
        return res;
    }
    int main()
    {
        scanf("%d%d%d",&n,&m,&p);
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&x[i],&y[i]);
        }
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&v[i]);
        }
        mask=(1<<n)-1;
        for(int i=1;i<=n;i++)
        {
            lg[1<<(i-1)]=i;
        }
        for(int i=1;i<=mask;i++)
        {	
            int sum=0;
            for(int j=0;j<n;j++)
            {
                if(i&(1<<j))
                {
                    w[i]+=v[j+1];
                    sum++;
                }
            }
            int num=sum;
            inv[i]=quick_pow(w[i],mod-2);
            for(int j=1;j<=n;j++)
            {
            	fa[j]=j;
            	s[j]=0;
            }
            for(int j=1;j<=m;j++)
            {
            	if(((1<<(x[j]-1))&i)&&((1<<(y[j]-1))&i))
            	{
            		int u=find(x[j]);
            		int v=find(y[j]);
            		if(u!=v)
            		{
            			sum--;
            			fa[u]=v;
            		}
            		s[x[j]]++;
            		s[y[j]]++;
            	}
            }
            int flag=0;
            for(int j=0;j<n;j++)
            {
            	if((1<<j)&i)
            	{
            		flag|=(s[j+1]&1);
            	}
            }
            if(flag||sum>1)
            {
            	g[num][i]=quick_pow(w[i],p);
            }
        }
        f[0][0]=1;
        FWT(f[0],1);
        for(int i=0;i<=n;i++)
        {
            FWT(g[i],1);
        }
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=i;j++)
            {
                for(int k=0;k<=mask;k++)
                {
                    f[i][k]+=1ll*g[j][k]*f[i-j][k]%mod;
                    f[i][k]%=mod;
                }
            }
            FWT(f[i],-1);
            for(int k=0;k<=mask;k++)
            {
                f[i][k]=1ll*f[i][k]*quick_pow(inv[k],p)%mod;
            }
            if(i<n)
            {
                FWT(f[i],1);
            }
        }
        printf("%d",f[n][mask]);
    }
  • 相关阅读:
    uoj310. 【UNR #2】黎明前的巧克力
    ZJOI2015 幻想乡战略游戏
    bzoj4407 于神之怒加强版
    bzoj3309 DZY Loves Math
    CF613D Kingdom and its Cities
    bzoj3677 [Apio2014]连珠线
    CF961G Partitions
    loj6077. 「2017 山东一轮集训 Day7」逆序对
    bzoj4596 [Shoi2016]黑暗前的幻想乡
    CF786B/CF787D Legacy
  • 原文地址:https://www.cnblogs.com/Khada-Jhin/p/10151741.html
Copyright © 2020-2023  润新知