【WC2018】州区划分(FWT,动态规划)

题面

UOJ

洛谷

题解

首先有一个暴力做法(就有\(50\)分了)

先\(O(2^nn^2)\)预处理出每个子集是否合法,然后设\(f[S]\)表示当前的答案,每次枚举一个子集进行转移,得到方程:\(\displaystyle f[S]=(\frac{1}{W_s})^p\sum_{T\subset S}f[T]*(W_{S-T})^p*check[S-T]\)。

其中\(W\)表示权值和,\(check\)表示是否合法。

这样子的复杂度是\(O(3^n)\),然后似乎可以拿\(50\)分了。

后面那个东西很像一个卷积,然而直接\(or\)卷积的话,会算出很多我们不想要的东西。

听说这个玩意叫做子集卷积。

要做的就是\(\sum f[T]*g[W]*[T\cup W=S,T\cap W=\phi]\)

可以用二进制下\(1\)的个数来表示这个限制\(\sum f[T]*g[W]*[T\cup W=S,cnt(T)+cnt(W)=cnt(S)]\)。

其中\(cnt(S)\)表示\(S\)中\(1\)的个数。

然后听说这个玩意就是一个套路了。。。

把集合的\(1\)的个数强制作为一维状态加上去。也就是\(f[cnt(S)][S]\)这样子。

令\(g[cnt(S)][S]=(W_S)^p*check[S]\)。

这样子的话只需要枚举两者的\(1\)的个数就可以去掉位的限制,然后就只剩下两者的交是\(S\)的限制,这个限制可以直接表示为或卷积(异或似乎也行???)。

那么直接\(FWT\)按层处理即可。

#include<iostream>
#include<cstdio>
using namespace std;
#define MOD 998244353
#define MAX 25
void add(int &x,int y){x+=y;if(x>=MOD)x-=MOD;}
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
int fpow(int a,int b)
{
int s=1;
while(b){if(b&1)s=1ll*s*a%MOD;a=1ll*a*a%MOD;b>>=1;}
return s;
}
int n,m,p,S,G[MAX],dg[MAX],lg[1<<21];
bool chk[1<<21];
int cc[1<<21];
int f[22][1<<21],g[22][1<<21],tmp[1<<21];
int w[MAX],W[1<<21],Wk[1<<21],invW[1<<21];
void FWT(int *a,int opt)
{
for(int i=1;i<S;i<<=1)
for(int p=i<<1,j=0;j<S;j+=p)
for(int k=0;k<i;++k)
if(opt==1)add(a[i+j+k],a[j+k]);
else add(a[i+j+k],MOD-a[j+k]);
}
int fa[MAX];
int getf(int x){return x==fa[x]?x:fa[x]=getf(fa[x]);}
int main()
{
n=read();m=read();p=read();S=1<<n;
for(int i=2;i<S;++i)lg[i]=lg[i>>1]+1;
for(int i=1;i<=m;++i)
{
int u=read()-1,v=read()-1;
G[u]|=1<<v;G[v]|=1<<u;
}
for(int i=0;i<n;++i)w[i]=read();
for(int i=1;i<S;++i)cc[i]=cc[i>>1]+(i&1);
for(int i=0;i<S;++i)
{
for(int j=0;j<n;++j)fa[j]=j;
for(int j=0;j<n;++j)if(i&(1<<j))dg[j]=cc[G[j]&i];
for(int j=0;j<n;++j)
if(i&(1<<j))
{
int x=i&G[j];
while(x)fa[getf(j)]=getf(lg[x&(-x)]),x-=x&(-x);
}
chk[i]=false;int nw=0;
for(int j=0;j<n;++j)if(i&(1<<j))W[i]+=w[j],nw=j;
for(int j=0;j<n&&!chk[i];++j)if((dg[j]&1)||((i&(1<<j))&&dg[j]==0))chk[i]=true;
for(int j=0;j<n;++j)dg[j]=0;
for(int j=0;j<n&&!chk[i];++j)if(i&(1<<j))if(getf(j)!=getf(nw))chk[i]=true;
if(cc[i]==1)chk[i]=false;
Wk[i]=fpow(W[i],p);invW[i]=fpow(Wk[i],MOD-2);
if(chk[i])g[cc[i]][i]=Wk[i];
}
f[0][0]=1;FWT(f[0],1);
for(int i=1;i<=n;++i)
{
FWT(g[i],1);
for(int j=0;j<i;++j)
for(int k=0;k<S;++k)
add(tmp[k],1ll*f[j][k]*g[i-j][k]%MOD);
FWT(tmp,-1);
for(int j=0;j<S;++j)f[i][j]=1ll*tmp[j]*invW[j]%MOD,tmp[j]=0;
if(i!=n)FWT(f[i],1);
}
/*
f[0]=1;
for(int i=1;i<S;++i)
{
for(int t=(i-1)&i;;t=(t-1)&i)
{
if(chk[i^t])add(f[i],1ll*f[t]*Wk[i^t]%MOD);
if(!t)break;
}
f[i]=1ll*f[i]*invW[i]%MOD;
}
*/
printf("%d\n",f[n][S-1]);
return 0;
}

【WC2018】州区划分(FWT,动态规划)的更多相关文章

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

    题目链接: [WC2018]州区划分 题目大意:给n个点的一个无向图,点有点权,要求将这n个点划分成若干个部分,每部分合法当且仅当这部分中所有点之间的边不能构成欧拉回路.对于一种划分方案,第i个部分的 ...

  2. [WC2018]州区划分(FWT)

    题目描述 题解 这道题的思路感觉很妙. 题目中有一个很奇怪的不合法条件,貌似和后面做题没有什么关系,所以我们先得搞掉它. 也就是判断一个点集是否合法,也就是判断这个点集是否存在欧拉回路. 如果存在欧拉 ...

  3. [WC2018]州区划分(FWT,FST)

    [WC2018]州区划分(FWT,FST) Luogu loj 题解时间 经典FST. 在此之前似乎用到FST的题并不多? 首先预处理一个子集是不是欧拉回路很简单,判断是否连通且度数均为偶数即可. 考 ...

  4. [WC2018]州区划分

    [WC2018]州区划分 注意审题: 1.有序选择 2.若干个州 3.贡献是州满意度的乘积 枚举最后一个州是哪一个,合法时候贡献sum[s]^p,否则贡献0 存在欧拉回路:每个点都是偶度数,且图连通( ...

  5. [UOJ#348][WC2018]州区划分

    [UOJ#348][WC2018]州区划分 试题描述 小 \(S\) 现在拥有 \(n\) 座城市,第ii座城市的人口为 \(w_i\),城市与城市之间可能有双向道路相连. 现在小 \(S\) 要将这 ...

  6. P4221 [WC2018]州区划分 无向图欧拉回路 FST FWT

    LINK:州区划分 把题目中四个条件进行规约 容易想到不合法当前仅当当前状态是一个无向图欧拉回路. 充要条件有两个 联通 每个点度数为偶数. 预处理出所有状态. 然后设\(f_i\)表示组成情况为i的 ...

  7. Luogu4221 WC2018州区划分(状压dp+FWT)

    合法条件为所有划分出的子图均不存在欧拉回路或不连通,也即至少存在一个度数为奇数的点或不连通.显然可以对每个点集预处理是否合法,然后就不用管这个奇怪的条件了. 考虑状压dp.设f[S]为S集合所有划分方 ...

  8. [WC2018]州区划分(状压DP+FWT/FMT)

    很裸的子集反演模板题,套上一些莫名其妙的外衣. 先预处理每个集合是否合法,再作显然的状压DP.然后发现可以写成子集反演的形式,直接套模板即可. 子集反演可以看这里. 子集反演的过程就是多设一维代表集合 ...

  9. uoj#348/洛谷P4221 [WC2018]州区划分(FWT)

    传送门(uoj) 传送门(洛谷) 全世界都会子集卷积就咱不会--全世界都在写\(FMT\)就咱只会\(FWT\)-- 前置芝士 或运算\(FWT\)或者\(FMT\) 左转洛谷模板区,包教包会 子集卷 ...

  10. [LOJ#2340] [WC2018] 州区划分

    题目链接 洛谷题面. LOJ题面.还是LOJ机子比较快 Solution 设\(f(s)\)表示选\(s\)这些城市的总代价,那么我们可以得到一个比较显然的\(dp\): \[ f(s)=\frac{ ...

随机推荐

  1. Python并发编程

    进程 相关概念 进程 进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础.在早期面向进程设计的计算机结构中,进程是程序的基本 ...

  2. javascript中的 return false和return true

    关于javascript中的 return false和return true,return 是javascript里函数返回值的关键字,一个函数内处理的结果可以使用return 返回,这样在调用函数 ...

  3. java的强制类型转换

    java强制类型转换  详细连接https://www.cnblogs.com/kuangwong/p/6198862.html 在Java项目的实际开发和应用中,常常需要用到将对象转为String这 ...

  4. python之路--内置函数, 匿名函数

    一 . 内置函数 什么是内置函数? 就是python给你提供的. 拿来直接⽤的函数, 比如print., input等等. 字符串类型代码的执⾏ eval() 执⾏字符串类型的代码. 并返回最终结果( ...

  5. synchronized与volatile的区别及各自的作用、原理(学习记录)

    synchronized与volatile的区别,它们的作用及原理? 说到两者的区别,先要了解锁提供的两种特性:互斥(mutual exclusion) 和可见性(visibility). 互斥:即一 ...

  6. drf实现图片验证码功能

    一.背景 在之前实现过django的图片验证码,有自己实现过的,也有基于django-simple-captcha的,都是基于form表单验证,若自己实现,可以获取相应的标签name便可以获取判断,若 ...

  7. python学习笔记(5-1)-基本数据类型-字符串类型及操作

    五.字符串处理函数  len(x):字符串x的长度.如len("12345")结果为5  str(x):任意类型x所对应的字符串形式. >>> str(123) ...

  8. Windows开启WMI时一些总结

    通过远程的方式连接WMI获取计算机信息时,可能会出现远程主机拒绝访问,这时就要通过下面的方式来开启当前计算机的WMI服务,下面以Win7和Win10为例来进行相关的说明,通过一步步排查去连接远程服务. ...

  9. ES 6 系列 - 赋值的新方式:解构赋值

    变量的解构赋值 es 6 允许按照一定的模式,从数组和对象中提取值,然后对变量进行赋值,这被称之为解构: 一.数组的解构赋值 最基本写法: let [a, b, c] = [1, 2, 3]; a / ...

  10. Directory of X:\EFI\Microsoft\Boot

    Directory of X:\EFI\Microsoft\Boot 2017/06/21 15:14 <DIR> . 2017/06/21 15:14 <DIR> .. 20 ...