[LOJ#2017][轮廓线DP][KMP]「SCOI2016」围棋
看到 \(m\le 12\) 和 \(c\le 6\) ,容易想到状压 DP
考虑转化成 \(3^{nm}\) 减去不合法的方案数,轮廓线 DP :\(f[i][j][S][k][h]\) 表示 DP 到了第 \(i\) 行第 \(j\) 格,轮廓线上 \(m\) 格的状态为 \(S\) 的方案数,\(k\) 表示最大的 \(x\) 使得模板矩阵第一行的前 \(x\) 个字符和目标矩阵第 \(i-1\) 行的后 \(x\) 个字符相同,\(h\) 表示最大的 \(x\) 使得模板矩阵第二行的前 \(x\) 个字符和目标矩阵第 \(i\) 行的后 \(x\) 个字符相同
于是我们每次枚举下一个格子填什么字符,\(k\) 和 \(h\) 利用 KMP 进行转移,任何时候保证 \(k\) 和 \(h\) 不全为 \(c\) 即可
但是这样做的复杂度为 \(O(qnmc^23^m)\) ,无法通过此题
我们发现复杂度主要消耗在 \(3^m\) 上,考虑如何才能不储存前面每个字符的状态
考虑仍然逐行转移 \(f[i][S]\) 表示前 \(i\) 行状态为 \(S\) ,但这里的 \(S\) 是一个集合,表示如果第 \(i\) 行以第 \(x\) 个格子为结尾的长度为 \(c\) 的子串与模板矩阵第一行相等,那么 \(x\in S\) ,否则 \(x\notin S\)
然后每一列内逐格转移:\(g[j][S][k][h]\) 表示前 \(j\) 个格子,轮廓线上一排格子状态为 \(S\) ,并且前 \(j\) 个格子的后缀与模板矩阵第一行和第二行前缀匹配的最大长度分别为 \(k\) 和 \(h\) 的方案数
边界 \(g[0][S][0][0]=f[i-1][S]\)
还是每次枚举下一个格子填什么,注意若 \(g[j][S][k][h]\) 满足 \(j+1\in S\) 且 \(h\) 下一步转移到了 \(c\) 则此步不能转移,若 \(k\) 下一步转移到了 \(c\) 则转移后的 \(S\) 集合中包含 \(j+1\) ,否则不包含
由于 \(S\) 中的任意元素都在 \([c,m]\) 内,故复杂度 \(O(qnmc^22^{m-c})\) ,可以通过此题
Code
#include <bits/stdc++.h>
const int N = 105, E = 14, C = (1 << 12) + 5, R = 8, rqy = 1e9 + 7;
const char ch[] = {'W', 'B', 'X'};
int n, m, c, q, Cm, f[N][C], g[E][C][R][R], nxt[2][R], tr[2][R][3], sum = 1;
char s[2][R];
void KMP(int n, char *s, int *nxt)
{
nxt[1] = 0;
for (int i = 2, j = 0; i <= n; i++)
{
while (j && s[i] != s[j + 1]) j = nxt[j];
if (s[i] == s[j + 1]) j++;
nxt[i] = j;
}
}
inline void add(int &a, const int &b)
{
a += b; if (a >= rqy) a -= rqy;
}
int DP()
{
f[0][0] = 1;
for (int i = 1; i <= n; i++)
{
for (int j = 0; j <= m; j++)
for (int S = 0; S < Cm; S++)
for (int k = 0; k <= c; k++)
for (int h = 0; h <= c; h++)
g[j][S][k][h] = 0;
for (int S = 0; S < Cm; S++)
g[0][S][0][0] = f[i - 1][S];
for (int j = 0; j < m; j++)
for (int S = 0; S < Cm; S++)
for (int k = 0; k <= c; k++)
for (int h = 0; h <= c; h++)
{
if (!g[j][S][k][h]) continue;
for (int w = 0; w < 3; w++)
{
int T = S;
if (j >= c - 1)
{
if (((S >> j - c + 1) & 1) && tr[1][h][w] == c)
continue;
T &= Cm - 1 ^ (1 << j - c + 1);
if (tr[0][k][w] == c) T |= 1 << j - c + 1;
}
add(g[j + 1][T][tr[0][k][w]][tr[1][h][w]], g[j][S][k][h]);
}
}
for (int S = 0; S < Cm; S++)
for (int k = 0; k <= c; k++)
for (int h = 0; h <= c; h++)
add(f[i][S], g[m][S][k][h]);
}
int res = 0;
for (int S = 0; S < Cm; S++) add(res, f[n][S]);
return res;
}
int main()
{
std::cin >> n >> m >> c >> q; Cm = 1 << m - c + 1;
for (int i = 1; i <= n * m; i++) sum = 3ll * sum % rqy;
while (q--)
{
for (int i = 0; i < 2; i++) scanf("%s", s[i] + 1);
KMP(c, s[0], nxt[0]); KMP(c, s[1], nxt[1]);
memset(f, 0, sizeof(f));
for (int T = 0; T < 2; T++)
for (int i = 0; i <= c; i++)
for (int w = 0; w < 3; w++)
{
int j = i;
while (j && s[T][j + 1] != ch[w]) j = nxt[T][j];
tr[T][i][w] = s[T][j + 1] == ch[w] ? j + 1 : j;
}
printf("%d\n", (sum - DP() + rqy) % rqy);
}
return 0;
}
[LOJ#2017][轮廓线DP][KMP]「SCOI2016」围棋的更多相关文章
- 「SCOI2016」围棋 解题报告
「SCOI2016」围棋 打CF后困不拉基的,搞了一上午... 考虑直接状压棋子,然后发现会t 考虑我们需要上一行的状态本质上是某个位置为末尾是否可以匹配第一行的串 于是状态可以\(2^m\)压住了, ...
- 【LOJ】#2017. 「SCOI2016」围棋
题解 考虑到状态数比较复杂,其实我们需要轮廓线dp-- 我们设置\(f[x][y][S][h][k]\)为考虑到第(x,y)个格子,S是轮廓线上的匹配状态,是二进制,如果一位是1表示这一位匹配第一行匹 ...
- loj#2013. 「SCOI2016」幸运数字 点分治/线性基
题目链接 loj#2013. 「SCOI2016」幸运数字 题解 和树上路径有管...点分治吧 把询问挂到点上 求出重心后,求出重心到每个点路径上的数的线性基 对于重心为lca的合并寻味,否则标记下传 ...
- loj#2015. 「SCOI2016」妖怪 凸函数/三分
题目链接 loj#2015. 「SCOI2016」妖怪 题解 对于每一项展开 的到\(atk+\frac{dnf}{b}a + dnf + \frac{atk}{a} b\) 令$T = \frac{ ...
- loj#2016. 「SCOI2016」美味
题目链接 loj#2016. 「SCOI2016」美味 题解 对于不带x的怎么做....可持久化trie树 对于带x,和trie树一样贪心 对于答案的二进制位,从高往低位贪心, 二进制可以表示所有的数 ...
- loj#2012. 「SCOI2016」背单词
题目链接 loj#2012. 「SCOI2016」背单词 题解 题面描述有点不清楚. 考虑贪心 type1的花费一定不会是优的,不考虑, 所以先把后缀填进去,对于反串建trie树, 先填父亲再填儿子, ...
- loj #2013. 「SCOI2016」幸运数字
#2013. 「SCOI2016」幸运数字 题目描述 A 国共有 n nn 座城市,这些城市由 n−1 n - 1n−1 条道路相连,使得任意两座城市可以互达,且路径唯一.每座城市都有一个幸运数字,以 ...
- 「SCOI2016」妖怪 解题报告
「SCOI2016」妖怪 玄妙...盲猜一个结论,然后过了,事后一证,然后假了,数据真水 首先要最小化 \[ \max_{i=1}^n (1+k)x_i+(1+\frac{1}{k})y_i \] \ ...
- 「SCOI2016」美味 解题报告
「SCOI2016」美味 状态极差无比,一个锤子题目而已 考虑每次对\(b\)和\(d\)求\(c=d \ xor \ (a+b)\)的最大值,因为异或每一位是独立的,所以我们可以尝试按位贪心. 如果 ...
随机推荐
- 搭建个人/企业私有云盘-seafile
一.安装依赖组件 安装前的准备工作安装 Seafile 服务器之前,请确认已安装以下软件MariaDB 或者 MySQL 服务器 (MariaDB 是 MySQL 的分支),python 2.7 (从 ...
- c++ vector 的坑
一个空的vector执行pop_back操作会发生什么 由于之前看STL源码剖析的时候,发现所执行的操作如下: 只是简单的将末尾的finish迭代器减1后destroy.这让人产生一个疑问:假如这个v ...
- 超简单!pytorch入门教程(三):构造一个小型CNN
torch.nn只接受mini-batch的输入,也就是说我们输入的时候是必须是好几张图片同时输入. 例如:nn. Conv2d 允许输入4维的Tensor:n个样本 x n个色彩频道 x 高度 x ...
- Linux 批量安装依赖
1.依赖检测失败,xxx被xxxx需要. 当我安装rpm 的时候,出现依赖检测失败. 我们可以到http://rpmfind.net/linux/rpm2html/search.php 这个网站上去搜 ...
- DEVOPS技术实践_20:串联多个job执行
在jenkins可能会有战役中场景,就是在一个job执行完之后,把这个执行结果作为另一个job的执行条件 比如A执行完,如果A执行成功,则执行B,如果失败则执行C 1 前期准备 A任务 import ...
- tensorflow之tf.train.exponential_decay()指数衰减法
exponential_decay(learning_rate, global_steps, decay_steps, decay_rate, staircase=False, name=None) ...
- ApacheHudi常见问题汇总
欢迎关注公众号:ApacheHudi 1. ApacheHudi对个人和组织何时有用 如果你希望将数据快速提取到HDFS或云存储中,Hudi可以提供帮助.另外,如果你的ETL /hive/spark作 ...
- HDU4507 吉哥系列故事——恨7不成妻 题解 数位DP
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4507 题目大意: 找到区间 \([L,R]\) 范围内所有满足如下条件的数的 平方和 : 不包含'7' ...
- 1023 组个最小数 (20 分)C语言
给定数字 0-9 各若干个.你可以以任意顺序排列这些数字,但必须全部使用.目标是使得最后得到的数尽可能小(注意 0 不能做首位).例如:给定两个 0,两个 1,三个 5,一个 8,我们得到的最小的数就 ...
- 让你彻底明白TCP三次握手,四次挥手
今天我们来讲一下TCP的三次握手和四次挥手,先来张思维导图. 一.TCP是什么 TCP(Transmission Control Protocol 传输控制协议)是一种面向连接的.可靠的.基于字节流 ...