习题:州区划分(子集卷积)

题目

传送门

思路

题目说了一大堆,实际上就是指一个州中的城市不存在欧拉回路

比较明显,有一个\(dp\)式,设\(dp[s]\)表示集合\(s\)的城市的所有方案的满意度之和

\(dp[s]=\sum_{s'|a=s,s'\&a=\phi}dp[s']*\frac{p_a}{p_s}\)

其中\(p_s\)表示集合\(s\)的人数和

对于一个定值\(s\),移下项,

则有\(p_sdp[s]=\sum_{s'|a=s,s'\&a=\phi}dp[s']p_a\)

然后暴力子集卷积就可以了,时间复杂度\(O(n2^n)\)

代码

#pragma GCC optimize(2)
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
using namespace std;
namespace FWT
{
    const int mod=998244353;
    void fwt(int *a,int l,int r)
    {
        if(r==l)
            return;
        int mid=(l+r)>>1,len=(r-l+1)>>1;
        fwt(a,l,mid);fwt(a,mid+1,r);
        for(int i=l;i<=mid;i++)
            a[i+len]=(a[i+len]+a[i])%mod;
    }
    void idfwt(int *a,int l,int r)
    {
        if(r==l)
            return;
        int mid=(l+r)>>1,len=(r-l+1)>>1;
        for(int i=l;i<=mid;i++)
            a[i+len]=((a[i+len]-a[i])%mod+mod)%mod;
        idfwt(a,l,mid);idfwt(a,mid+1,r);
    }
}
namespace ufs
{
    int tot;
    int fa[25];
    void makeset(int n)
    {
        for(int i=0;i<=n;i++)
            fa[i]=i;
    }
    int findset(int x)
    {
        if(fa[x]==x)
            return x;
        return fa[x]=findset(fa[x]);
    }
    void merge(int a,int b)
    {
        a=findset(a);
        b=findset(b);
        if(a!=b)
            tot--;
        fa[a]=b;
    }
}
using namespace FWT;
using namespace ufs;
int n,m,p;
int d[25];
int w[25],s[(1<<21)+5],inv[(1<<21)+5];
int val[25][(1<<21)+5];
int dp[25][(1<<21)+5];
int mem[(1<<21)+5];
bool can[(1<<21)+5];
pair<int,int> e[505];
long long qkpow(int a,int b)
{
    if(b==0)
        return 1;
    if(b==1)
        return a;
    long long t=qkpow(a,b/2);
    t=t*t%mod;
    if(b&1)
        t=t*a%mod;
    return t;
}
void clear(int n)
{
	tot=0;
    makeset(n);
    for(int i=0;i<n;i++)
        d[i]=0;
}
bool check(int s)
{
    for(int i=0;i<n;i++)
        if((s&(1<<i))&&(d[i]&1))
            return 1;
    return 0;
}
int getcnt(int w)
{
    if(mem[w]!=-1)
        return mem[w];
    int tot=0;
    while(w)
    {
        if(w&1)
            tot++;
        w>>=1;
    }
    return mem[w]=tot;
}
int main()
{
    memset(mem,-1,sizeof(mem));
    cin>>n>>m>>p;
    for(int i=1;i<=m;i++)
    {
        cin>>e[i].first>>e[i].second;
        e[i].first--;e[i].second--;
    }
    for(int i=0;i<n;i++)
        cin>>w[i];
    for(int i=0;i<(1<<n);i++)
    {
        clear(n);
        for(int j=0;j<n;j++)
        	if(i&(1<<j))
        	{
        		tot++;
    			s[i]+=w[j];
			}
        for(int j=1;j<=m;j++)
            if((i&(1<<e[j].first))&&(i&(1<<e[j].second)))
            {
                d[e[j].first]++;d[e[j].second]++;
                merge(e[j].first,e[j].second);
            }
        inv[i]=qkpow(qkpow(s[i],mod-2),p);
        if(tot!=1||check(i))
        {
            can[i]=1;    
            val[getcnt(i)][i]=qkpow(s[i],p);
        }
    }
    dp[0][0]=1;
    fwt(dp[0],0,(1<<n)-1);
    fwt(val[0],0,(1<<n)-1);
    for(int i=1;i<=n;i++)
    {
        fwt(val[i],0,(1<<n)-1);
        for(int j=0;j<i;j++)
            for(int k=0;k<(1<<n);k++)
                dp[i][k]=(dp[i][k]+1ll*dp[j][k]*val[i-j][k]%mod)%mod;
        idfwt(dp[i],0,(1<<n)-1);
        for(int j=0;j<(1<<n);j++)
        {
            if(getcnt(j)!=i)
                dp[i][j]=0;
            dp[i][j]=1ll*dp[i][j]*inv[j]%mod;
        }
        fwt(dp[i],0,(1<<n)-1);
    }
    idfwt(dp[n],0,(1<<n)-1);
    cout<<dp[n][(1<<n)-1];
    return 0;
}
/*
10 33 2
3 1
4 1
5 1
1 6
7 1
8 1
1 9
10 1
3 2
4 2
2 5
6 2
7 2
2 8
9 2
10 2
3 4
3 6
7 3
8 3
3 9
5 4
4 6
9 4
5 8
5 9
10 5
6 8
6 9
7 8
9 7
9 8
10 9
71 92 25 73 2 76 25 82 48 54

*/
posted @ 2021-02-03 17:09  loney_s  阅读(76)  评论(0)    收藏  举报