真的是个好(毒)题(瘤)。其中枚举的思想尤其值得借鉴。

\(40pts\):插头\(dp\),记录插头的同时记录每一列的连接状况,复杂度\(O(N*M*2^{n + m} )\)。

\(100pts\):容斥\(+\)插头\(/\)轮廓线。目前要维护每两行和每两列的限制,我们把两个限制分开讨论。预处理一下每个子矩阵如果不作限制的可行方案,然后人为地进行限制分割。对于列的限制用容斥解决,对于行的限制套在里面逐行枚举做一次\(dp\),就可以把复杂度降到可以接受的\(O(m^3*2^n)\)。

代码有借鉴网上。并非完全原创。

\(40pts\):

#include <bits/stdc++.h>
using namespace std; const int N = 15 + 5;
const int base = 2999830;
const int Mod = 19901013;
const int M = 3000000 + 5;
typedef unsigned int uint; int n, m, cur, las, cnt[2], can[N][N]; int nxt[M], head[M], dp[2][M]; uint Hash[2][M]; int get_wei (uint zt, int wei) {
return (zt >> wei) % 2;
} int alt_wei (uint zt, int wei, int val) {
return zt + ((0ll + val - get_wei (zt, wei)) << wei);
} void update (uint zt, int val) {
uint _zt = zt % base;
for (int i = head[_zt]; i; i = nxt[i]) {
if (Hash[cur][i] == zt) {
(dp[cur][i] += val) %= Mod; return;
}
}
nxt[++cnt[cur]] = head[_zt];
head[_zt] = cnt[cur];
Hash[cur][cnt[cur]] = zt;
// cout << "dp[" << cur << "][" << cnt[cur] << "] = " << val << endl;
dp[cur][cnt[cur]] = val;
} int get_val (uint zt) {
uint _zt = zt % base;
for (int i = head[_zt]; i; i = nxt[i]) {
if (Hash[cur][i] == zt) {
return dp[cur][i];
}
}
return 0;
} void change_row () {
for (int i = 1; i <= cnt[cur]; ++i) {
uint &zt = Hash[cur][i];
if (get_wei (zt, m + m) == 0) {
dp[cur][i] = 0;//到不了下一行
}
for (int i = m; i >= 1; --i) {
zt = alt_wei (zt, i, get_wei (zt, i - 1));
}
zt = alt_wei (zt, 0, 0); // 全都向后移一位
zt = alt_wei (zt, m + m, 0); // 到下一行的标记清空
}
} int solve () {
update (1 << (m + m), 1);
for (int i = 1; i <= n; ++i) {
change_row ();
for (int j = 1; j <= m; ++j) {
// cout << "i = " << i << " j = " << j << endl;
las = cur, cur ^= 1, cnt[cur] = 0;
memset (head, 0, sizeof (head));
for (int k = 1; k <= cnt[las]; ++k) {
uint zt = Hash[las][k];
int b1 = get_wei (zt, j - 1);
int b2 = get_wei (zt, j - 0);
int val = dp[las][k];
if (val == 0) continue; // 不转移的小优化
// cout << "b1 = " << b1 << " b2 = " << b2 << endl;
if (!can[i][j]) {
if (!b1 && !b2) {
update (zt, val);
}
} else {
if (b1 == 0 && b2 == 0) {
if (can[i][j + 1]) {
uint _zt = zt;
_zt = alt_wei (_zt, j - 0, 1); // 右插头改为 1
_zt = alt_wei (_zt, m + j, 1); // j 列和 j + 1 列相连
update (_zt, val);
}
if (can[i + 1][j]) {
uint _zt = zt;
_zt = alt_wei (_zt, j - 1, 1); // 下插头改为 1
_zt = alt_wei (_zt, m + m, 1); // 和下一行连通
update (_zt, val);
}
update (zt, val); // 注意你并不需要放满所有没有障碍的格子
}
if (b1 + b2 == 1) { // 有一个连入的插头
uint _zt = zt;
_zt = alt_wei (_zt, j - 0, 0);
_zt = alt_wei (_zt, j - 1, 0);
update (_zt, val);
}
}
}
// cout << "las : " << endl;
// for (int k = 1; k <= cnt[las]; ++k) {
// cout << "state = " << Hash[las][k] << " val = " << dp[las][k] << endl;
// }
// cout << "cur : " << endl;
// for (int k = 1; k <= cnt[cur]; ++k) {
// cout << "state = " << Hash[cur][k] << " val = " << dp[cur][k] << endl;
// }
// cout << endl;
}
}
uint fin = 0;
for (int i = m + 1; i < m + m; ++i) {
fin |= (1 << i);
}
return get_val (fin);
} int main () {
cin >> n >> m;
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
int ch = getchar ();
while (ch != '.' && ch != 'x') {
ch = getchar ();
}
if (ch == '.') {
can[i][j] = true;
}
}
}
// for (int i = 1; i <= n; ++i) {
// for (int j = 1; j <= m; ++j) {
// cout << can[i][j] << " ";
// }
// cout << endl;
// }
cout << solve () << endl;
}

\(100pts\):

#include <bits/stdc++.h>
using namespace std; const int N = 15 + 5;
const int M = 40000 + 5;
const int Mod = 19901013; int n, m, top, ans;
int f[N], num[N], tmp[2][M], dp[N][N][N][N]; bool mp[N][N]; int ask (int u, int v) {
return (u >> v) % 2;
} void work (int w, int u, int v) {
int las = 0, tot = (1 << (v - u + 1)) - 1;
for (int i = 0; i <= tot; ++i) {
tmp[0][i] = 0;
}
int t2 = 0;
for (int i = u; i <= v; ++i) {
if (mp[w][i]) {
t2 |= (1 << (i - u));
}
}
tmp[0][t2] = 1;
for (int i = w + 1; i <= m + 1; ++i) {
int zt = 0;
for (int j = u, t = 0; j <= v; ++j, ++t) {
zt |= (mp[i][j] << t);
int cur = las ^ 1;
for (int k = 0; k <= tot; ++k) tmp[cur][k] = 0;
for (int k = 0; k <= tot; ++k) {
if(!tmp[las][k]) continue;
int t2 = k;
if (ask(t2,t) != mp[i][j]) {
t2 ^= (1 << t);
}
(tmp[cur][t2] += tmp[las][k]) %= Mod;
if (ask (k, t)) continue;
if (j != v && !ask (k, t + 1)) {
t2 = k | (1 << (t + 1));
if (ask (t2, t) != mp[i][j]) {
t2 ^= (1 << t);
}
(tmp[cur][t2] += tmp[las][k]) %= Mod;
}
if(!mp[i][j]) {
t2 = k | (1 << t);
(tmp[cur][t2] += tmp[las][k]) %= Mod;
}
}
las = cur;
}
dp[w][u][i - 1][v] = tmp[las][zt];
}
} int main () {
cin >> m >> n;
for(int i = 1; i <= m; ++i) {
for (int j = 1; j <= n; ++j) {
int ch = getchar ();
while (ch != '.' && ch != 'x') {
ch = getchar ();
}
mp[i][j] = (ch == 'x');
}
}
for (int i = 1; i <= n; ++i) {
for (int j = i; j <= n; ++j) {
for (int k = 1; k <= m; ++k) {
work (k, i, j);
}
}
} int tot = (1 << (n - 1)) - 1;
for(int i = 0; i <= tot; ++i) {
int top = 1;
num[top] = 0;
for (int j = 0; j < n - 1; ++j) {
if (ask (i, j)) num[++top] = j + 1;
}
num[++top] = n;
for (int d = 1; d <= m; ++d) {
for (int u = 1; u <= d; ++u) {
int t = 1;
for(int j = 2; j <= top; ++j) {
int p = num[j - 1] + 1, q = num[j];
t = (1ll * t * dp[u][p][d][q]) % Mod;
}
if (u == 1) {
f[d] = t;
} else {
f[d] = (0ll + f[d] - (1ll * t * f[u - 1])) % Mod;
}
}
}
(f[m] += Mod) %= Mod;
if (top % 2 == 1) {
(ans -= f[m]) %= Mod;
} else {
(ans += f[m]) %= Mod;
}
}
cout << (ans + Mod) % Mod << endl;
}

Luogu P2595 [ZJOI2009]多米诺骨牌 容斥,枚举,插头dp,轮廓线dp的更多相关文章

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

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

  2. 【做题】51NOD1518 稳定多米诺覆盖——容斥&dp

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

  3. 「ZJOI2009」多米诺骨牌

    「ZJOI2009」多米诺骨牌 题目描述 有一个n × m 的矩形表格,其中有一些位置有障碍.现在要在这个表格内 放一些1 × 2 或者2 × 1 的多米诺骨牌,使得任何两个多米诺骨牌没有重叠部分,任 ...

  4. 【Tsinghua OJ】多米诺骨牌(domino)问题

    (domino.c/cpp)[问题描述] 小牛牛对多米诺骨牌有很大兴趣,然而她的骨牌比较特别,只有黑色和白色的两种.她觉 得如果存在连续三个骨牌是同一种颜色,那么这个骨牌排列便是不美观的.现在她有n个 ...

  5. 省选训练赛第4场D题(多米诺骨牌)

    题目来自FZU2163 多米诺骨牌 Time Limit: 1000 mSec    Memory Limit : 32768 KB  Problem Description Vasya很喜欢排多米诺 ...

  6. 【01背包】洛谷P1282多米诺骨牌

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

  7. 多米诺骨牌放置问题(状压DP)

    例题: 最近小A遇到了一个很有趣的问题: 现在有一个\(n\times m\)规格的桌面,我们希望用\(1 \times 2\)规格的多米诺骨牌将其覆盖. 例如,对于一个\(10 \times 11\ ...

  8. P1282 多米诺骨牌 (背包变形问题)

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

  9. [LeetCode] Push Dominoes 推多米诺骨牌

    There are N dominoes in a line, and we place each domino vertically upright. In the beginning, we si ...

随机推荐

  1. nginx代理,负载均衡

    #代理,如:通过代理实现访问百度(单个机器)创建vim proxy.conf内容如下server { listen 80; server_name www.baidu.com; location / ...

  2. Linux下源码安装MySQL-5.6.25

    从mysql-5.5起,mysql源码安装开始使用cmake了,因此我们得先安装cmake,配置安装目录./configure --perfix=/.....的时候和以前的会有些区别. 一.安装cma ...

  3. 清晰理解redux中的

    首先需要明白 Redux 的单一状态树的概念,所谓的单一状态树,就是指“所有的 state 都以一个对象树的形式储存在一个单一的 store 中.” 比如我们有这么一个状态树(或者你叫它状态对象也行) ...

  4. java反射机制学习笔记

    内容引用自:https://www.cnblogs.com/wkrbky/p/6201098.html https://www.cnblogs.com/xumBlog/p/8882489.html,本 ...

  5. CF486B OR in Matrix(构造+思维)

    CF486B 一道有趣的思维题 由于or的性质可知只要a[i][j]为1那么b中第i行,第j列将都变成1 相反的,如果b[i][j]是0那么a中第i行,第j列都必须是0 根据第二个性质我们可以构造出a ...

  6. Maven - Maven3实战学习笔记(3)使用maven构建Web应用

    1.jetty-maven-plugin自动化测试Web应用工具 <plugin> <groupId>org.mortbay.jetty</groupId> < ...

  7. 小记---------kafka理论及命令行操作

    kafka-0.10.1.X版本之前: auto.offset.reset 的值为smallest,和,largest.(offest保存在zk中)   kafka-0.10.1.X版本之后: aut ...

  8. 关于eclipse设置JRebel

    版本:eclipse ee Version: 2018-09 (4.9.0) jrebel:最新2019-2 1.在eclipse->help->eclipse Marketplace 2 ...

  9. django1.9安装以及使用

    安装setuptoolswget --no-check-certificate https://pypi.python.org/packages/69/56/f0f52281b5175e3d9ca86 ...

  10. Kibana多用户创建及角色权限控制

    1 介绍 ELK日志管理属于基础设施平台,接入多个应用系统是正常现象,如果接入多个系统的索引文件没有进行权限划分,那么很大程度会出现索引文件误处理现象,为了避免这种情况发生,多用户及权限设置必不可少. ...