题意:求有多少种方案,用多米诺骨牌覆盖一个\(n\times m\)的棋盘,满足任意一对相邻行和列都至少有一个骨牌横跨。对\(10^9+7\)取模。

\(n,m \leq 16\)

首先,这个问题的约束比较复杂,直接dp需要较高的代价记录状态,不能通过本题。

然而,这个问题的约束可以被拆分为多个小约束(某条线被横跨),且小约束可以直接合并。这启发我们使用容斥。

这样,我们的dp计数就简化为了固定几条线不被跨越后任意覆盖。设\(f_k\)为恰有\(k\)条线不被跨越的方案数,\(g_k\)为我们计算出的固定了\(k\)条线后的覆盖方案数。那么,我们有

\[g_k = \sum_{i \geq k} {{i}\choose{k}} f_i
\]

由二项式反演可得

\[f_0 = \sum_{k \geq 0} (-1)^k g_k
\]

剩下的问题就在于计算所有\(g_k\)了。我们不能枚举所有要被跨越的线,但是枚举一维之后,另一维就可以dp了。假设我们已经枚举了列上的线,令\(dp_{i,j}\)表示前\(i\)行有\(j\)条线没有跨越的方案数,暴力转移。通过预处理能做到\(O(n^3)\)。而考虑到状态中的\(j\)至于最后\(-1\)的指数有关,故可以省去。因此这个dp是\(O(n^2)\)的。

时间复杂度\(O(n^2 2^n)\)。

#include <bits/stdc++.h>
using namespace std;
const int N = 20, MOD = (int)(1e9 + 7), MAX = 16;
int n,m,dp[2][1 << MAX],f[N][N],ans,g[N],rec[N];
typedef long long ll;
void doit(int wd) {
memset(dp,0,sizeof dp);
int p = 1, lim = (1 << wd) - 1;
dp[0][(1 << wd)-1] = 1;
for (int i = 1 ; i <= n ; ++ i) {
for (int j = 1 ; j <= wd ; ++ j, p ^= 1) {
memset(dp[p],0,sizeof dp[p]);
for (int s = 0 ; s < (1 << wd) ; ++ s) {
if (((s >> (wd-1))&1) == 0)
(dp[p][(s << 1 | 1) & lim] += dp[p^1][s]) %= MOD;
else {
if ((!(s&1)) && j != 1) (dp[p][(s << 1 | 3) & lim] += dp[p^1][s]) %= MOD;
(dp[p][(s << 1) & lim] += dp[p^1][s]) %= MOD;
}
}
}
f[i][wd] = dp[p^1][lim];
}
}
void prework() {
for (int i = 1 ; i <= m ; ++ i)
doit(i);
}
vector<int> tmp;
int main() {
n = m = 16;
prework();
while (scanf("%d%d",&n,&m) != EOF) {
ans = 0;
for (int s = (1 << m >> 1) ; s < (1 << m) ; ++ s) {
tmp.clear();
int las = 0;
for (int i = 1 ; i <= m ; ++ i)
if ((s >> (i-1))&1) tmp.push_back(i-las), las = i;
for (int i = 1 ; i <= n ; ++ i) {
rec[i] = 1;
for (int j = 0 ; j < (int)tmp.size() ; ++ j)
rec[i] = 1ll * rec[i] * f[i][tmp[j]] % MOD;
}
memset(g,0,sizeof g);
for (int i = 1 ; i <= n ; ++ i) {
g[i] = rec[i];
for (int k = 1 ; k < i ; ++ k)
(g[i] += -1ll * rec[i-k] * g[k] % MOD) %= MOD;
}
if (tmp.size()&1) (ans += g[n]) %= MOD;
else (ans -= g[n]) %= MOD;
}
ans = (ans % MOD + MOD) % MOD;
printf("%d\n",ans);
}
return 0;
}

小结:本题的关键在于想到容斥,以及枚举一维后dp另一维。这两个思路都有较广的适用性,有必要熟练运用。

【做题】51NOD1518 稳定多米诺覆盖——容斥&dp的更多相关文章

  1. 51Nod1518 稳定多米诺覆盖 动态规划 插头dp 容斥原理

    原文链接https://www.cnblogs.com/zhouzhendong/p/51Nod1518.html 题目传送门 - 51Nod1518 题意 51Nod真是个好OJ ,题意概括的真好, ...

  2. 51nod 1518 稳定多米诺覆盖(容斥+二项式反演+状压dp)

    [传送门[(http://www.51nod.com/Challenge/Problem.html#!#problemId=1518) 解题思路 直接算不好算,考虑容斥,但并不能把行和列一起加进去容斥 ...

  3. Luogu P2595 [ZJOI2009]多米诺骨牌 容斥,枚举,插头dp,轮廓线dp

    真的是个好(毒)题(瘤).其中枚举的思想尤其值得借鉴. \(40pts\):插头\(dp\),记录插头的同时记录每一列的连接状况,复杂度\(O(N*M*2^{n + m} )\). \(100pts\ ...

  4. P1282 多米诺骨牌 (差值DP+背包)

    题目描述 多米诺骨牌有上下2个方块组成,每个方块中有1~6个点.现有排成行的 上方块中点数之和记为S1,下方块中点数之和记为S2,它们的差为|S1-S2|.例如在图8-1中,S1=6+1+1+1=9, ...

  5. jzoj5987. 【WC2019模拟2019.1.4】仙人掌毒题 (树链剖分+概率期望+容斥)

    题面 题解 又一道全场切的题目我连题目都没看懂--细节真多-- 先考虑怎么维护仙人掌.在线可以用LCT,或者像我代码里先离线,并按时间求出一棵最小生成树(或者一个森林),然后树链剖分.如果一条边不是生 ...

  6. 【做题】POI2011R1 - Plot——最小圆覆盖&倍增

    原文链接 https://www.cnblogs.com/cly-none/p/loj2159.html 题意:给出\(n\)个点,你需要按编号将其划分成不超过\(m\)段连续的区间,使得所有每个区间 ...

  7. 【做题】TCSRM601 Div1 500 WinterAndSnowmen——按位考虑&dp

    原文链接https://www.cnblogs.com/cly-none/p/9695526.html 题意:求有多少对集合\(S,T\)满足:\(S \subseteq \{1,2...n \}, ...

  8. 【做题】uoj#370滑稽树上滑稽果——巧妙dp

    一个显然的结论是最终树的形态必然是一条链.具体证明只要考虑选定树上的某一条链,然后把其他部分全部接在它后面,这样答案一定不会变劣. 那么,一开始的想法是考虑每一位的最后出现位置,但这并不容易实现.注意 ...

  9. 洛谷P1282 多米诺骨牌【线性dp】

    题目:https://www.luogu.org/problemnew/show/P1282 题意: 给定n个牌,每个牌有一个上点数和下点数.可以通过旋转改变交换上下点数. 问使得上点数之和和下点数之 ...

随机推荐

  1. Uva297 Quadtrees【递归建四分树】【例题6-11】

    白书 例题6-11 用四分树来表示一个黑白图像:最大的图为根,然后按照图中的方式编号,从左到右对应4个子结点.如果某子结点对应的区域全黑或者全白,则直接用一个黑结点或者白结点表示:如果既有黑又有白,则 ...

  2. field_automation源码分析

    field_automation主要通过uvm_field_utils_begin和uvm_field_utils_end来加到自己写的class中. uvm_field_utils_begin,在o ...

  3. 十 js中forEach,for in,for of循环的用法

    一.一般的遍历数组的方法: var array = [1,2,3,4,5,6,7]; for (var i = 0; i < array.length; i++) { console.log(i ...

  4. node.js中express模块创建服务器和http模块客户端发请求

    首先下载express模块,命令行输入 npm install express 1.node.js中express模块创建服务端 在js代码同文件位置新建一个文件夹(www_root),里面存放网页文 ...

  5. linux ~/ 和 /

    /是目录层的分隔.表示符.只有一个/表明是root,/etc/表明是根目录下面的etc目录(当然目录最后不需要/,但有/直接表明他是目录,没有末尾的/,那么/etc需要检测一下确定是目录还是文件,虽然 ...

  6. A stock

    1. 密集成交不太妙 主力抛压退为好

  7. 排名函数——ROW_NUMBER()、RANK()、DENSE_RANK()和NTILE(n)

    ROW_NUMBER()函数:行号,根据作为参数传递给这个函数的ORDER BY子句的值,返回一个不断递增的整数值.如果ROW_NUMBER的ORDER BY的值和结果集中的顺序相匹配,返回值将是递增 ...

  8. tomcat2章1

    package ex02.pyrmont; import java.io.File; public class Constants { public static final String WEB_R ...

  9. ajax处理文件下载

    ajax中处理文件下载,可能大数会遇到我和一样的问题,什么问题呢?就是下载程序执行了,但是浏览器没有任何下载操作,这是为什么呢? 那是因为response原因,一般请求浏览器是会处理服务器输出的res ...

  10. css实现16:9的图片比例

    摘自:https://www.cnblogs.com/caizhenbo/p/css.html 需求: 最近产品要求不管原图的大小是多少,宽度一定,高度要自自适应为16:9. 分析: 对于正常的固定好 ...