习题:州区划分(子集卷积)
题目
思路
题目说了一大堆,实际上就是指一个州中的城市不存在欧拉回路
比较明显,有一个\(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
*/