博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Luogu P2595 [ZJOI2009]多米诺骨牌 容斥,枚举,插头dp,轮廓线dp
阅读量:6079 次
发布时间:2019-06-20

本文共 7404 字,大约阅读时间需要 24 分钟。

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

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

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

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

\(40pts\)

#include 
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 
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

你可能感兴趣的文章
退休延迟致新老职员共事 澳大利亚管理者面临挑战
查看>>
适当时公布?新西兰会否重启父母团聚移民引关注
查看>>
春运中的“洋导游”
查看>>
探访高铁“火花侠”驾驶火龙专列 脚下钢花飞溅
查看>>
2019年美联储加息若放缓有何影响?外汇局回应
查看>>
2018年访日外国游客消费创新高 中国大陆居首
查看>>
瓜子二手车保障消费新举措 首家12315维权服务站于呼市成立
查看>>
2019CBA全明星周末举行正赛 南方明星队获胜
查看>>
韩国最大比特币交易所Bithumb被黑客攻击,损失超过350亿韩元
查看>>
如何在 Scala 中利用 ADT 良好地组织业务
查看>>
几种常见的CSS布局
查看>>
Netflix最新视频优化实践:用更少的带宽打造完美画质
查看>>
基于Spring Boot实现图片上传/加水印一把梭操作
查看>>
关于js、jq零碎知识点
查看>>
有赞跨平台长连接组件设计及可插拔改造
查看>>
高德,腾讯地图 --> 逆地址解析(坐标位置描述)
查看>>
nodejs流之行读取器例子
查看>>
源码|HDFS之NameNode:启动过程
查看>>
[译] 什么是Javascript中的提升
查看>>
阿里巴巴、百度、腾讯都在用的Java架构师知识体系
查看>>