题目分析:

听说这题考场上能被$ O(4^n) $的暴力水过,难不成出题人是毕姥爷?

首先思考一个显而易见的$ O(n^2*2^n) $的暴力DP。一般的DP都是考虑最近的加入了哪个点,然后删除后递归进行状压DP。由于这道题的题目询问方式是反过来的,处理方式也反过来。

令$ f[n][S] $表示当前有$ S $这些点,期望这些点能够构成独立集大小为$ n $。正向的考虑选择了哪个点,并把与这个点有连边的所有点在集合内进行删除,令找到的新状态为$ f[n-1][P] $。我们把$ P $中的结点与$ S $中不在$ P $中的点进行标号拼接。写成语言就是$ f[n][S]+=f[n-1][P] * \binom{|S|-1}{|P|} * |P|! $ 由于对于$ S $的每一个点都需要转移一遍,时间复杂度就变成了$ O(n^2*2^n) $。虽然跑得不快,但是由于冗余状态较多,考场上一定比例的人使用这个算法通过了这个题。

现在来考虑把它优化到$ O(n*2^n) $。由于题目期望着你获得一个最大独立集,所以我们可以发现第一维是没有必要的。因为对于一个目标状态$ S $,我们如果知道$ S $对应的最大独立集的大小的话,那么我们必定是奔着这个大小而去的。现在我们用$ g[S] $来表示$ S $对应的最大独立集大小,那么这是一个普及组题目,枚举选点然后求max就行了。再对于$ f[S] $,求$ S $对应的最大独立集大小。首先记录$ g[S] $,然后找删除某个点后的集合变为了$ g[S]-1 $的就是我们想要的转移方案,同样采用带标号的拼接。因为我们没有了第一维的负担,所以时间复杂度骤降为了$ O(n*2^n) $.

ps:我终于会用letax数学公式啦。

代码:

 #include<bits/stdc++.h>
using namespace std; const int maxn = ;
const int mod = ; int n,m;
int connect[maxn];
int f[(<<)+],g[(<<)+],sz[(<<)+];
int arr[(<<)+],C[maxn][maxn],fac[]; int fast_pow(int now,int pw){
if(pw == ) return now;
int z = fast_pow(now,pw/);
z = (1ll*z*z) % mod;
if(pw & ) z = (1ll*z*now)%mod;
return z;
} void read(){
scanf("%d%d",&n,&m);
for(int i=;i<=m;i++){
int u,v; scanf("%d%d",&u,&v);
connect[u] |= (<<v-);
connect[v] |= (<<u-);
}
for(int i=;i<=n;i++) connect[i] |= (<<i-);
} void init(){
C[][] = ;
for(int i=;i<=n;i++){
C[i][] = C[i][i] = ;
for(int j=;j<i;j++) C[i][j] = (C[i-][j-]+C[i-][j])%mod;
}
for(int i=;i<(<<n);i++){
for(int j=;j<n;j++) if((<<j)&i) sz[i]++;
}
fac[] = ;
for(int i=;i<=n;i++) fac[i] = (1ll*fac[i-]*i)%mod;
} void dfs(int now){
arr[now] = ;
for(int i=;i<=n;i++){
if(!((<<i-)&now)) continue;
int p = now - (now&connect[i]);
if(!arr[p]) dfs(p);
g[now] = max(g[now],g[p]+);
}
} void dfs2(int now){
arr[now] = ;
for(int i=;i<=n;i++){
if(!((<<i-)&now)) continue;
int kk = (now&connect[i]),p = now - kk;
if(g[p] != g[now]-) continue;
if(!arr[p]) dfs2(p);
f[now]+=((1ll*C[sz[now]-][sz[p]]*fac[sz[kk]-])%mod)*f[p]%mod;
f[now] %= mod;
}
} void work(){
init();
g[] = ; arr[] = ;
for(int i=;i<(<<n);i++) if(!arr[i]) dfs(i);
memset(arr,,sizeof(arr));
f[] = ; arr[] = ;
dfs2((<<n)-);
int ans = f[(<<n)-];
ans = (1ll*fast_pow(fac[n],mod-)*ans)%mod;
printf("%d",ans);
} int main(){
read();
work();
return ;
}

LOJ2540 [PKUWC2018] 随机算法 【状压DP】的更多相关文章

  1. LOJ2540 PKUWC2018 随机算法 状压DP

    传送门 两种$DP$: ①$f_{i,j}$表示前$i$次选择,最大独立集为$j$时达到最大独立集的方案总数,转移:$a.f_{i,j}+=f_{i+1,j+2^k}$(保证$k$加入后符合条件):$ ...

  2. [LOJ2540] [PKUWC2018] 随机算法

    题目链接 LOJ:https://loj.ac/problem/2540 Solution 写的时候脑子不太清醒码了好长然后时间\(LOJ\)垫底... 反正随便状压\(dp\)一下就好了,设\(f[ ...

  3. 洛谷 P4547 & bzoj 5006 随机二分图 —— 状压DP+期望

    题目:https://www.luogu.org/problemnew/show/P4547 https://www.lydsy.com/JudgeOnline/problem.php?id=5006 ...

  4. loj2540 「PKUWC2018」随机算法 【状压dp】

    题目链接 loj2540 题解 有一个朴素三进制状压\(dp\),考虑当前点三种状态:没考虑过,被选入集合,被排除 就有了\(O(n3^{n})\)的转移 但这样不优,我们考虑优化状态 设\(f[i] ...

  5. 【洛谷5492】[PKUWC2018] 随机算法(状压DP)

    点此看题面 大致题意: 用随机算法求一张图的最大独立集:每次随机一个排列,从前到后枚举排列中的点,如果当前点加入点集中依然是独立集,就将当前点加入点集中,最终得到的点集就是最大独立集.求这个随机算法的 ...

  6. 「算法笔记」状压 DP

    一.关于状压 dp 为了规避不确定性,我们将需要枚举的东西放入状态.当不确定性太多的时候,我们就需要将它们压进较少的维数内. 常见的状态: 天生二进制(开关.选与不选.是否出现--) 爆搜出状态,给它 ...

  7. 算法笔记-状压dp

    状压dp 就是把状态压缩的dp 这样还是一种暴力但相对于纯暴力还是优雅的多. 实际上dp就是经过优化的暴力罢了 首先要了解位运算 给个链接吧 [https://blog.csdn.net/u01337 ...

  8. Luogu4547 THUWC2017 随机二分图 概率、状压DP

    传送门 考虑如果只有$0$组边要怎么做.因为$N \leq 15$,考虑状压$DP$.设$f_i$表示当前的匹配情况为$i$时的概率($i$中$2^0$到$2^{N-1}$表示左半边的匹配情况,$2^ ...

  9. 算法复习——状压dp

    状压dp的核心在于,当我们不能通过表现单一的对象的状态来达到dp的最优子结构和无后效性原则时,我们可能保存多个元素的有关信息··这时候利用2进制的01来表示每个元素相关状态并将其压缩成2进制数就可以达 ...

随机推荐

  1. LiveCharts文档-3开始-3类型和设置

    原文:LiveCharts文档-3开始-3类型和设置 LiveCharts文档-3开始-3类型和设置 类型和设置 这一部分非常的重要,涉及到LiveCharts的基本构成单元的介绍 LiveChart ...

  2. SQL SERVER中DBLINK的实现

    exec sp_addlinkedserver 'CantennFlexPsApp' , '' , 'SQLOLEDB' , '目标数据库的IP' exec sp_addlinkedsrvlogin ...

  3. Luogu P2279 [HNOI2003]消防局的设立

    这真的是一道SB题.去你的树形DP 我们看到题目就开始考虑贪心,怎么搞? 一个显然的思路,每次找出一个深度最大且未被覆盖的点,然后建一个消防局? 但这样的话,动用简单的人类思维就可以知道:我TM的还不 ...

  4. [Spark][Hive][Python][SQL]Spark 读取Hive表的小例子

    [Spark][Hive][Python][SQL]Spark 读取Hive表的小例子$ cat customers.txt 1 Ali us 2 Bsb ca 3 Carls mx $ hive h ...

  5. Python从菜鸟到高手(1):初识Python

    1 Python简介 1.1 什么是Python   Python是一种面向对象的解释型计算机程序设计语言,由荷兰人吉多·范罗苏姆(Guido van Rossum)于1989年发明,第一个公开发行版 ...

  6. JAVA核心:内存、比较和Final

    1.java是如何管理内存的 java的内存管理就是对象的分配和释放问题.(其中包括两部分) 分配:内存的分配是由程序完成的,程序员需要通过关键字new为每个对象申请内存空间(基本类型除外),所有的对 ...

  7. KVM虚拟机管理——资源调整

    1. 概述2. 计算资源调整2.1 调整处理器配置2.2 调整内存配置3. 存储资源调整3.1 根分区扩展3.2 添加磁盘4. 网络资源调整 1. 概述 KVM在使用过程中,会涉及到计算(CPU,内存 ...

  8. 分布式监控系统Zabbix-批量添加聚合图形

    之前部署了Zabbix(3.4.4版本)监控环境,由于主机比较多,分的主机组也比较多,添加聚合图形比较麻烦,故采用python脚本进行批量添加聚合图形.脚本下载地址:https://pan.baidu ...

  9. 最近新明白的SQL小知识

    1.partition by和order by 先看三个小需求: ①查询出各个类编号的书本的数量. select count (类编号) as 数量, 类编号 from Books group by ...

  10. oracle系统化学习笔记

    CentOS 4.x上安装Oracle 9i(3讲) oracle9i非常成熟,刚学主要是学9i比较经典 学会安装9i具有现实意义,先学完9i再学11g等比较好 1.安装centos 2.安装orac ...