题意:求有多少种方案,用多米诺骨牌覆盖一个\(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. caffe中通过prototxt文件查看神经网络模型结构的方法

    在修改propotxt之前我们可以对之前的网络结构进行一个直观的认识: 可以使用http://ethereon.github.io/netscope/#/editor 这个网址. 将propotxt文 ...

  2. callback源码分析——callback_iter和callback

    uvm_callback_iter,定义了function,first,last,next,prev的函数, 其中定义的还是相应uvm_callbacks的静态函数: 所以之前uvm_callback ...

  3. codeforces 979A Pizza, Pizza, Pizza!!!

    题意: 对一个圆形的pizza,只能用直线来切它,求把它切为n+1份的形状和size都相同的最下次数. 思路: 形状和size都相同,那么只能是扇形,分奇偶讨论. n为0还得特判,切0刀,因为这个还被 ...

  4. sql privot 实现行转列

    表结构如下: RefID    HRMS    Name    InsuranceMoney    InsuranceNamefb2bdee8-e4c9-4470-8e7f-14550d3212f7  ...

  5. weblogic 乱码

    1.找到weblogic安装目录,当前项目配置的domain 2.找到bin下的setDomainEnv.cmd文件 3.打开文件,从文件最后搜索第一个set JAVA_OPTIONS=%JAVA_O ...

  6. Win7 Python开发环境搭建

    1.  下载Anaconda并安装 地址: https://www.anaconda.com/download/ Anaconda包括Python基础包与一系列科学计算包,安装后不用再单独安装Pyth ...

  7. highchart应用示例1--2个不同类型变量2个y轴

    1.ajax调用接口和处理数据 function getCityData() { var date1 = $('#datetimepicker1').val(); var date2 = $('#da ...

  8. Mybatis 在 insert 之后想获取自增的主键 id

    记录一次傻逼的问题, 自己把自己蠢哭:Mybatis 在 insert 之后想获取自增的主键 id,但却总是返回1 错误说明: 返回的1是影响的行数,并不是自增的主键id: 想要获取自增主键id,需要 ...

  9. Git在Eclipse中的使用

    一.把远程仓库的项目clone到eclipse里面: 最新版的Eclipse上已经集成了Git插件.所以在Eclipse中可以很方便的使用Git的功能. 在使用Git功能之前,需要先进行下简单的设置. ...

  10. SharePoint 解决管理员密码修改后各种问题的来袭

    问题描述:本人用的是Win10自带虚拟机Hyper-V.Windows Service 2012R2.SQL2014和SharePoint2016,由于有一段时间没有登录虚拟机,在此登录的时候提示密码 ...