题目:http://uoj.ac/problem/348

一开始可以 3^n 子集DP,枚举一种状态的最后一个集合是什么来转移;

设 \( f[s] \) 表示 \( s \) 集合内的点都划分好了,\( g[s] = \sum\limits_{i \in s} w[i] \)

那么 \( f[s] = \sum\limits_{d \subseteq s} \frac{f[s-d] * g[d]}{g[s]} \)

注意判断一个集合是否合法,不仅要判断每个点的度数,还要判断整个集合是否连通;

这样就可以过 n <= 15 的点了,UOJ上有30分;

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
int const xn=(<<)+,xxn=,xm=,mod=;
int rd()
{
int ret=,f=; char ch=getchar();
while(ch<''||ch>''){if(ch=='-')f=; ch=getchar();}
while(ch>=''&&ch<='')ret=ret*+ch-'',ch=getchar();
return f?ret:-ret;
}
int n,m,p,f[xn],g[xn],w[xxn],g2[xn];
int hd[xxn],ct,to[xm],nxt[xm],bin[xxn];
void add(int x,int y){to[++ct]=y; nxt[ct]=hd[x]; hd[x]=ct;}
int upt(int x){while(x>=mod)x-=mod; while(x<)x+=mod; return x;}
ll pw(ll a,int b){ll ret=; for(;b;b>>=,a=a*a%mod)if(b&)ret=ret*a%mod; return ret;}
bool vis[xxn];
int dfs(int x,int s)
{
vis[x]=; int ret=;
for(int i=hd[x],u;i;i=nxt[i])
if(!vis[u=to[i]]&&(s&bin[u-]))ret+=dfs(u,s);
return ret;
}
bool ck(int s)//
{
int cnt=;
for(int x=;x<=n;x++)
{
if(!(s&bin[x-]))continue;
int deg=; cnt++;
for(int i=hd[x];i;i=nxt[i])
{
if(s&bin[to[i]-])deg++;
}
if(deg&)return ;
}
for(int i=;i<=n;i++)vis[i]=;
for(int i=;i<=n;i++)
if(s&bin[i-])return dfs(i,s)!=cnt;
}
int main()
{
n=rd(); m=rd(); p=rd();
bin[]=; for(int i=;i<=n;i++)bin[i]=bin[i-]*;
for(int i=,x,y;i<=m;i++)x=rd(),y=rd(),add(x,y),add(y,x);
for(int i=;i<=n;i++)g[bin[i-]]=rd();
for(int s=;s<bin[n];s++)g[s]=upt(g[s&(-s)]+g[s^(s&(-s))]);
for(int s=;s<bin[n];s++)g[s]=pw(g[s],p),g2[s]=pw(g[s],mod-);
for(int s=;s<bin[n];s++)if(!ck(s))g[s]=;
int num=;
f[]=;
for(int s=;s<bin[n];s++)
{
for(int d=s;d;d=(s&(d-)))//d=s
f[s]=(f[s]+(ll)f[s^d]*g[d])%mod;
f[s]=(ll)f[s]*g2[s]%mod;
}
printf("%d\n",f[bin[n]-]);
return ;
}

3^n

关于FMT(其实和高维前缀和差不多)和子集卷积:https://www.cnblogs.com/Dance-Of-Faith/p/8818211.html

于是可以做子集卷积加速DP的过程。

代码如下:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
int const xn=(<<)+,xxn=,xm=,mod=;
int rd()
{
int ret=,f=; char ch=getchar();
while(ch<''||ch>''){if(ch=='-')f=; ch=getchar();}
while(ch>=''&&ch<='')ret=ret*+ch-'',ch=getchar();
return f?ret:-ret;
}
int n,m,p,f[xxn][xn],g[xxn][xn],w[xxn],g2[xn];
int hd[xxn],ct,to[xm],nxt[xm],bin[xxn],cnt[xn];
void add(int x,int y){to[++ct]=y; nxt[ct]=hd[x]; hd[x]=ct;}
int upt(int x){while(x>=mod)x-=mod; while(x<)x+=mod; return x;}
ll pw(ll a,int b){ll ret=; for(;b;b>>=,a=a*a%mod)if(b&)ret=ret*a%mod; return ret;}
bool vis[xxn];
int dfs(int x,int s)
{
vis[x]=; int ret=;
for(int i=hd[x],u;i;i=nxt[i])
if(!vis[u=to[i]]&&(s&bin[u-]))ret+=dfs(u,s);
return ret;
}
bool ck(int s)//
{
int cnt=;
for(int x=;x<=n;x++)
{
if(!(s&bin[x-]))continue;
int deg=; cnt++;
for(int i=hd[x];i;i=nxt[i])
{
if(s&bin[to[i]-])deg++;
}
if(deg&)return ;
}
for(int i=;i<=n;i++)vis[i]=;
for(int i=;i<=n;i++)
if(s&bin[i-])return dfs(i,s)!=cnt;
}
int cal(int s){int ret=; while(s)ret+=(s&),s>>=; return ret;}
void fmt(int *a,int tp)
{
for(int d=;d<bin[n];d<<=)
for(int s=;s<bin[n];s++)
if(s&d)a[s]=upt(a[s]+a[s^d]*tp);
}
int main()
{
n=rd(); m=rd(); p=rd();
bin[]=; for(int i=;i<=n;i++)bin[i]=bin[i-]*;
for(int i=,x,y;i<=m;i++)x=rd(),y=rd(),add(x,y),add(y,x);
for(int s=;s<bin[n];s++)cnt[s]=cal(s);
for(int i=;i<=n;i++)g2[bin[i-]]=rd();
for(int s=;s<bin[n];s++)g2[s]=upt(g2[s&(-s)]+g2[s^(s&(-s))]);
for(int s=;s<bin[n];s++)g[cnt[s]][s]=pw(g2[s],p),g2[s]=pw(g[cnt[s]][s],mod-);
for(int s=;s<bin[n];s++)if(!ck(s))g[cnt[s]][s]=;
for(int i=;i<=n;i++)fmt(g[i],);
f[][]=; fmt(f[],);
for(int i=;i<=n;i++)
{
for(int j=;j<=i;j++)
for(int s=;s<bin[n];s++)
f[i][s]=(f[i][s]+(ll)f[j][s]*g[i-j][s])%mod;
fmt(f[i],-);
for(int s=;s<bin[n];s++)
if(cnt[s]==i)f[i][s]=(ll)f[i][s]*g2[s]%mod;
else f[i][s]=;
fmt(f[i],);
}
fmt(f[n],-);
printf("%d\n",f[n][bin[n]-]);
return ;
}

UOJ #348 州区划分 —— 状压DP+子集卷积的更多相关文章

  1. UOJ348 WC2018 州区划分 状压DP、欧拉回路、子集卷积

    传送门 应该都会判欧拉回路吧(雾 考虑状压DP:设\(W_i\)表示集合\(i\)的点的权值和,\(route_i\)表示点集\(i\)的导出子图中是否存在欧拉回路,\(f_i\)表示前若干个城市包含 ...

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

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

  3. 【UOJ348】【WC2018】州区划分 状压DP FWT

    题目大意 给定一个\(n\)个点的无向图,对于每种 \(n\) 个点的划分\(\{S_1,S_2,\ldots,S_k\}\),定义它是合法的,当且仅当每个点都在其中的一个集合中且对于任何的\(i\i ...

  4. HDU6321 Dynamic Graph Matching【状压DP 子集枚举】

    HDU6321 Dynamic Graph Matching 题意: 给出\(N\)个点,一开始没有边,然后有\(M\)次操作,每次操作加一条无向边或者删一条已经存在的边,问每次操作后图中恰好匹配\( ...

  5. [WC2018]州区划分(状压,子集卷积)

    [洛谷题面]https://www.luogu.org/problemnew/show/P4221 首先考虑判定一个子图是否合法: (1)连通:并查集判断即可. (2)没有欧拉回路:存在欧拉回路的条件 ...

  6. UOJ#348 州区划分

    解:有一个很显然的状压...... 就设f[s]表示选的点集为s的时候所有方案的权值和. 于是有f[s] = f[s \ t] * (sum[t] / sum[s])P. 这枚举子集是3n的. 然后发 ...

  7. 集合划分状压dp

    给一个 $n$ 个点 $m$ 条边的无向图,每条边有 $p_i$ 的概率消失,求图连通的概率 $n \leq 9$ sol: 我们考虑一个 $dp$ $f_{(i,S)}$ 表示只考虑前 $i$ 条边 ...

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

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

  9. 洛谷P3959 宝藏(NOIP2017)(状压DP,子集DP)

    洛谷题目传送门 Dalao的题解多数是什么模拟退火.DFS剪枝.\(O(3^nn^2)\)的状压DP之类.蒟蒻尝试着把状压改进了一下使复杂度降到\(O(3^nn)\). 考虑到每条边的贡献跟它所在的层 ...

随机推荐

  1. java拾遗3----XML解析(三) StAX PULL解析

    使用PULL方式解析XML: Pull是STAX的一个实现 StAX是The Streaming API for XML的缩写,一种利用拉模式解析(pull-parsing)XML文档的API StA ...

  2. 九度OJ 1347:孤岛连通工程 (最小生成树)

    时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:1522 解决:314 题目描述: 现在有孤岛n个,孤岛从1开始标序一直到n,有道路m条(道路是双向的,如果有多条道路连通岛屿i,j则选择最短 ...

  3. Activiti使用过程_1

    1 微信公众号:

  4. IntelliJ IDEA(2017)安装和破解(转发)

    IntelliJ IDEA(2017)安装和破解 IDEA 全称 IntelliJ IDEA,是Java语言开发的集成环境,IntelliJ在业界被公认为最好的java开发工具之一,尤其在智能代码助手 ...

  5. 中国移动OnetNet云平台 使用WIFI模块ESP8266 TCP透传模式传输数据流步骤

    测试使用工具: WIFI模块型号:ESP8266 https://item.taobao.com/item.htm?spm=a1z10.1-c.w137712-175513579.2.btbD9X&a ...

  6. linux删除文件夹的命令

    使用rm -rf 目录名字 命令即可 -r 就是向下递归,不管有多少级目录,一并删除-f 就是直接强行删除,不作任何提示的意思 eg 删除文件夹实例:rm -rf /var/log/httpd/acc ...

  7. LeetCode:移动零【283】

    LeetCode:移动零[283] 题目描述 给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序. 示例: 输入: [0,1,0,3,12] 输出: [1,3 ...

  8. CAS的实现Atomic类库

    atomic 原子(atomic)本意是"不能被进一步分割的最小粒子",而原子操作(atomic operation)意为"不可被中断的一个或一系列操作".在多 ...

  9. Linux系统中UI库curse.h不存在问题——贪吃蛇为例

    1. 问题 大家在用Linux写程序时,大家会使用Linux gcc编译器中的头文件curse.h.但往往一般的发行版中都没有默认安装这个头文件,需要大家自行安装.最近遇到这个问题,如下: Red  ...

  10. java WEB学习笔记32:HttpSession 接口常用方法 及 HttpServletRequest接口中的Session方法 Demo

    本博客为原创:综合 尚硅谷(http://www.atguigu.com)的系统教程(深表感谢)和 网络上的现有资源(博客,文档,图书等),资源的出处我会标明 本博客的目的:①总结自己的学习过程,相当 ...