# UOJ#348：【WC2018】州区划分 （FMT优化DP）

$f\left[s\right]=\sum _{t\in s,t\ne \mathrm{\varnothing }}f\left[s-t\right]\ast h\left[t\right]\ast \frac{sum\left[t{\right]}^{p}}{sum\left[s{\right]}^{p}}$

$f\left[s\right]\ast sum\left[s{\right]}^{p}=\sum _{t\in s,t\ne \mathrm{\varnothing }}f\left[s-t\right]\ast \left(h\left[t\right]\ast sum\left[t{\right]}^{p}\right)$

（其实这题的DP模型并不难，会FMT的话也能很快优化到时限以内，作为WC的题或许太简单了点QAQ）

CODE：

#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<stdio.h>
#include<algorithm>
using namespace std;

const int maxn=24;
const int maxs=(1<<21)+100;
const int M=998244353;

int F[maxn][maxs];
int f[maxn][maxs];
int g[maxn][maxs];

int cnt[maxs];
int Num[maxs];

int sum[maxs];
int rev_sum[maxs];
bool h[maxs];

int deg[maxn];
int fa[maxn];
int temp[maxn];
int cur;

bool e[maxn][maxn];
int w[maxn];
int n,m,k,N;

int G(int x)
{
return (1<<(x-1));
}

int Mod(int x)
{
if (x>=M) return x-M;
else return x;
}

void FMT(int *a)
{
for (int len=2; len<=N; len<<=1)
{
int mid=(len>>1);
for (int *p=a; p!=a+N; p+=len)
for (int i=0; i<mid; i++)
p[mid+i]=Mod(p[mid+i]+p[i]);
}
}

int Up(int x)
{
if (fa[x]==x) return x;
return (fa[x]=Up(fa[x]));
}

bool Judge(int s)
{
cur=0;
int num=0;
for (int i=1; i<=n; i++) if ( s&G(i) )
{
deg[i]=0;
fa[i]=i;
num++;
temp[++cur]=i;
}
for (int i=1; i<cur; i++)
for (int j=i+1; j<=cur; j++)
{
int x=temp[i];
int y=temp[j];
if (!e[x][y]) continue;
deg[x]++;
deg[y]++;
x=Up(x);
y=Up(y);
if (x==y) continue;
fa[x]=y;
num--;
}
bool flag=1;
for (int i=1; i<=cur; i++)
if (deg[ temp[i] ]&1) flag=0;
if ( flag && num==1 ) return 0;
return 1;
}

int Pow(int x,int y)
{
if (!y) return 1;
int temp=Pow(x,y>>1);
temp=(long long)temp*temp%M;
if (y&1) temp=(long long)temp*x%M;
return temp;
}

void UFMT(int *a)
{
for (int len=2; len<=N; len<<=1)
{
int mid=(len>>1);
for (int *p=a; p!=a+N; p+=len)
for (int i=0; i<mid; i++)
p[mid+i]=Mod(p[mid+i]-p[i]+M);
}
}

int main()
{
freopen("walk.in","r",stdin);
freopen("walk.out","w",stdout);

scanf("%d%d%d",&n,&m,&k);
for (int i=1; i<=m; i++)
{
int u,v;
scanf("%d%d",&u,&v);
e[u][v]=e[v][u]=1;
}
for (int i=1; i<=n; i++) scanf("%d",&w[i]);

N=(1<<n);
for (int i=1; i<=n; i++) Num[G(i)]=i;
for (int i=1; i<N; i++)
{
int j=(i&(-i));
sum[i]=sum[i^j]+w[ Num[j] ];
j^=i;
cnt[i]=cnt[j]+1;
rev_sum[i]=Pow(sum[i],M-2);
rev_sum[i]=Pow(rev_sum[i],k);
};
for (int i=1; i<N; i++) h[i]=Judge(i);

for (int i=1; i<N; i++) g[ cnt[i] ][i]=Pow(sum[i],k)*h[i];
for (int i=1; i<=n; i++) FMT(g[i]);

for (int i=0; i<N; i++) f[0][i]=1;
for (int i=1; i<=n; i++)
{
for (int j=1; j<=i; j++)
for (int s=0; s<N; s++) F[i][s]=((long long)F[i][s]+(long long)f[i-j][s]*g[j][s])%M;
UFMT(F[i]);
for (int s=0; s<N; s++) F[i][s]=(long long)F[i][s]*rev_sum[s]%M,f[i][s]=F[i][s];
FMT(f[i]);
}
printf("%d\n",F[n][N-1]);

return 0;
}

©️2019 CSDN 皮肤主题: 技术黑板 设计师: CSDN官方博客