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

\(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;
}

转载于:https://www.cnblogs.com/maomao9173/p/10832633.html

Luogu P2595 [ZJOI2009]多米诺骨牌 容斥,枚举,插头dp,轮廓线dp相关推荐

  1. 「ZJOI2009」多米诺骨牌

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

  2. 【动态规划】 多米诺骨牌 (ssl 1632/luogu 1282)

    多米诺骨牌多米诺骨牌多米诺骨牌 Description Input 输入文件的第一行是一个正整数n(1≤n≤1000),表示多米诺骨牌数.接下来的n行表示n个多米诺骨牌的点数.每行有两个用空格隔开的正 ...

  3. P1282-多米诺骨牌【dp,背包】

    正题 评测记录:https://www.luogu.org/recordnew/lists?uid=52918&pid=P1282 题目大意 n个多米诺骨牌,上下值不相同,可以交换一个多米诺上 ...

  4. C++ P1282 多米诺骨牌

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

  5. Blender多米诺骨牌动画学习教程 The Impossible Domino Run in Blender

    流派:电子学习| MP4 |视频:h264,1280×720 |音频:AAC,48.0 KHz 语言:英语+中英文字幕(根据原英文字幕机译更准确)|大小:8.53 GB 含课程文件 |时长:8h 20 ...

  6. S6 edge+的多米诺骨牌效应:大屏的趋势

    日前,为庆祝三星S6 edge+国行版的顺利发售,三星盖乐世社区的一些粉丝自发组织了三星疯狂"盖星人"第一期活动--活动现场除了可以对S6 edge+进行全方面体验之外,还将演示著 ...

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

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

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

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

  9. 多米诺骨牌v.1MEL语言

    // //  //Script Name:多米诺骨牌v.1 //Author:疯狂小猪  //Last Updated: 2011.10.5 //Email:wzybwj@163.com //  // ...

  10. leetcode 1128. 等价多米诺骨牌对的数量

    给你一个由一些多米诺骨牌组成的列表 dominoes. 如果其中某一张多米诺骨牌可以通过旋转 0 度或 180 度得到另一张多米诺骨牌,我们就认为这两张牌是等价的. 形式上,dominoes[i] = ...

最新文章

  1. Intent、Bundle
  2. 随机森林RF中的特征重要性的计算公式VIM
  3. 百万并发中间件系统的内核设计看Java并发性能优化
  4. 应用程序错误电脑黑屏_电脑黑屏了怎么办,电脑硬件win黑屏的6大原因以及解决方法...
  5. php session写入数据库_php session 写入数据库,phpsession
  6. 第二章 数学运算、数组、文字处理
  7. Servlet中的请求转发
  8. 自学SAP ERP步骤
  9. 什么是WBS分解法?
  10. z-blogPHP在西部数码虚拟主机上遇到WTS-WAF错误拦截情况,协商好久他们还是妥协了...
  11. linux开机启动grub rescue,Ubuntu 开机出现 grub rescue 的模式下修复
  12. iOS开发app打包
  13. 普通PC通过USB转485串口 ModBus-RTU通信协议控制伺服电机
  14. ftp linux 推送文件_Linux文件传输FTP详解
  15. 读《深度思考 让所有事情都能正确入手》
  16. java中算术异常值_依据拉依达准则来剔除异常值程序
  17. 研究表明:漂亮的配图会让数据/结果看起更可靠
  18. liblensfun 在 mingw 上编译时遇到的奇怪问题
  19. 当心收入被支出吞噬(zt)
  20. php对接短信宝,php使用短信宝发送短信的方法

热门文章

  1. 随机变换背景图象(一个可以刷新心情的特效)
  2. Excel-统计函数
  3. 在Javascript中,获取到数字超出长度问题
  4. HTML实现学习网站首页
  5. Layui 数据表格动态cols(字段)动态变化
  6. vue cli3.0 解决跨域问题和axios等问题,配合Django
  7. 面试疑点:几道题答了一个小时,应该是等答案
  8. 以字型为例,一维表示的二维数组矩阵,旋转90、-90
  9. 在老MAC系统上编译OpenJDK8,顺利通过
  10. 别人回答工作中的问题,要自行判断是否正确