「TJOI 2018」游园会 Party
「TJOI 2018」游园会 Party
题目描述
小豆参加了 \(NOI\) 的游园会,会场上每完成一个项目就会获得一个奖章,奖章只会是 \(N, O, I\) 的字样。 在会场上他收集到了 \(K\) 个奖章组成的串。兑奖规则是奖章串和兑奖串的最长公共子序列长度为小豆最后奖励的等级。 现在已知兑奖串长度为 \(N\) ,并且在兑奖串上不会出现连续三个奖章为
NOI,即奖章中不会出现子串NOI。 现在小豆想知道各个奖励等级会对应多少个不同的合法兑奖串。\(N \leq 1000, K \leq 15\)
解题思路 :
首先吐槽一下这个题和 「BZOJ 3864」 几乎没有任何区别,省选放这种题真的合适吗?
考虑一个比较 \(naive\) 的做法,用一个想当然的 \(dp\) 来对答案计数
\(f[i][j][s1][s2]\) 表示长度为 \(i\) 的兑奖串与奖章串的 \(lcs = j\) 且 \(i-1\) 上的字母为 \(s1\) ,\(i\) 上的字母为 \(s2\) 的方案数
观察发现,这个东西转移需要知道与奖章串每一个前缀的 \(lcs\) 是多少,不然 \(i+1\) 的时候无法转移
于是就要维护当前的一个状态集合 \(S\) ,\(S_j\) 表示奖章串前缀 \(j\) 与当且兑奖串的 \(lcs\)
可以对 \(S\) 做一遍 \(lcs\) 的 \(dp\) 从 \(f[i][S]\) 转移到 \(f[i+1][S']\) ,但是 \(S\) 有至多 \(15\) 位,每一位的值至多为 \(15\)
显然无法直接表示为状态,于是需要进行简化状态.
观察发现,对于任意一个状态集合 \(S\), \(S_j -S_{j-1} \leq 1\)
不妨对 \(S\) 进行差分,那么每一位的值域范围就是 \([0,1]\) 了,可以用一个至多 \(2^{15}\) 的数来表示
于是只需要用类似于 \(lcs\) 的 \(dp\) 预处理出来 \(S\) 之间的转移,并额外用一个 \(dp\) 对答案计数即可
\(f[i][S][s1][s2]\) 表示长度为 \(i\) 的兑奖串,与奖章串的每一个前缀的 \(lcs\) 状态集合为 \(S\), 且 \(i-1\) 上的字母为 \(s1\) ,\(i\) 上的字母为 \(s2\) 的方案数
设 \(S'\) 为 \(S\) 能转移到的状态,判一下新加的字符是否会和 \(s1, s2\) 形成 \(NOI\) 再转移即可
预处理的复杂度是 \(O(2^k \times k)\) ,\(dp\) 的复杂度是 \(O(n \times 2^k)\),总复杂度是 \(O(n \times 2^k)\)
/*program by mangoyang*/
#include<bits/stdc++.h>
#define inf (0x7f7f7f7f)
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
typedef long long ll;
using namespace std;
template <class T>
inline void read(T &x){
int f = 0, ch = 0; x = 0;
for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1;
for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
if(f) x = -x;
}
const int N = 1005, K = (1 << 15) + 10, Mod = 1000000007;
char str[N];
int f[2][K][3][3], a[N], s[N], Ans[N], n, m, all;
inline void up(int &x, int y){ x += y; if(x >= Mod) x %= Mod; }
struct Node{ int x, s1, s2; }; vector<Node> g[K][3][3];
namespace Prework{
inline void GetTrans(int x){
for(int i = 0; i < m; i++)
if((1 << i) & x) a[i+1] = a[i] + 1; else a[i+1] = a[i];
for(int i = 0; i < 3; i++)
for(int j = 0; j < 3; j++)
for(int k = 0; k < 3; k++)
if(i != 0 || j != 1 || k != 2){
int res = 0;
for(int p = 1, ls = 0, now; p <= m; p++, ls = now){
now = Max(a[p], a[p-1] + (s[p] == k));
if(now > ls) res |= (1 << p - 1); else now = ls;
}
g[x][i][j].push_back((Node){res, j, k});
}
}
inline void realmain(){ for(int i = 0; i <= all; i++) GetTrans(i); }
}
int main(){
read(n), read(m), all = (1 << m) - 1;
scanf("%s", str + 1);
for(int i = 1; i <= m; i++){
if(str[i] == 'N') s[i] = 0;
if(str[i] == 'O') s[i] = 1;
if(str[i] == 'I') s[i] = 2;
}
Prework::realmain();
f[0][0][2][2] = 1;
for(int i = 0, o = 0; i < n; i++, o ^= 1){
memset(f[o^1], 0, sizeof(f[o^1]));
for(int j = 0; j <= all; j++)
for(int s1 = 0; s1 < 3; s1++)
for(int s2 = 0; s2 < 3; s2++)
for(int k = 0; k < g[j][s1][s2].size(); k++){
Node now = g[j][s1][s2][k];
int nxt = now.x, s3 = now.s1, s4 = now.s2;
up(f[o^1][nxt][s3][s4], f[o][j][s1][s2]);
}
}
for(int j = 0; j <= all; j++){
int tot = 0;
for(int i = 0; i < m; i++) tot += (j >> i) & 1;
for(int s1 = 0; s1 < 3; s1++)
for(int s2 = 0; s2 < 3; s2++) up(Ans[tot], f[n&1][j][s1][s2]);
}
for(int i = 0; i <= m; i++) cout << Ans[i] << endl;
return 0;
}
「TJOI 2018」游园会 Party的更多相关文章
- 「TJOI 2018」教科书般的亵渎
「TJOI 2018」教科书般的亵渎 题目描述 小豆喜欢玩游戏,现在他在玩一个游戏遇到这样的场面,每个怪的血量为 \(a_i\) ,且每个怪物血量均不相同, 小豆手里有无限张"亵渎" ...
- LOJ #2542. 「PKUWC 2018」随机游走(最值反演 + 树上期望dp + FMT)
写在这道题前面 : 网上的一些题解都不讲那个系数是怎么推得真的不良心 TAT (不是每个人都有那么厉害啊 , 我好菜啊) 而且 LOJ 过的代码千篇一律 ... 那个系数根本看不出来是什么啊 TAT ...
- LOJ #2802. 「CCC 2018」平衡树(整除分块 + dp)
题面 LOJ #2802. 「CCC 2018」平衡树 题面有点难看...请认真阅读理解题意. 转化后就是,给你一个数 \(N\) ,每次选择一个 \(k \in [2, N]\) 将 \(N\) 变 ...
- LOJ #2541. 「PKUWC 2018」猎人杀(容斥 , 期望dp , NTT优化)
题意 LOJ #2541. 「PKUWC 2018」猎人杀 题解 一道及其巧妙的题 , 参考了一下这位大佬的博客 ... 令 \(\displaystyle A = \sum_{i=1}^{n} w_ ...
- LOJ #2540. 「PKUWC 2018」随机算法(概率dp)
题意 LOJ #2540. 「PKUWC 2018」随机算法 题解 朴素的就是 \(O(n3^n)\) dp 写了一下有 \(50pts\) ... 大概就是每个点有三个状态 , 考虑了但不在独立集中 ...
- LOJ #2538. 「PKUWC 2018」Slay the Spire (期望dp)
Update on 1.5 学了 zhou888 的写法,真是又短又快. 并且空间是 \(O(n)\) 的,速度十分优秀. 题意 LOJ #2538. 「PKUWC 2018」Slay the Spi ...
- loj#2054. 「TJOI / HEOI2016」树
题目链接 loj#2054. 「TJOI / HEOI2016」树 题解 每次标记覆盖整棵字数,子树维护对于标记深度取max dfs序+线段树维护一下 代码 #include<cstdio> ...
- 「TJOI / HEOI2016」字符串
「TJOI / HEOI2016」字符串 题目描述 佳媛姐姐过生日的时候,她的小伙伴从某东上买了一个生日礼物.生日礼物放在一个神奇的箱子中.箱子外边写了一个长为 \(n\) 的字符串 \(s\),和 ...
- AC日记——#2054. 「TJOI / HEOI2016」树
#2054. 「TJOI / HEOI2016」树 思路: 线段树: 代码: #include <cstdio> #include <cstring> #include < ...
随机推荐
- Codeforces 765F Souvenirs
time limit per test 3 seconds memory limit per test 512 megabytes input standard input output standa ...
- thinkphp表单验证
之前的表单验证都是用js写的,这里也可以使用tp框架的验证.但是两者比较而言还是js验证比较好,因为tp框架验证会运行后台代码,这样运行速度和效率就会下降. 自动验证是ThinkPHP模型层提供的一种 ...
- 使sqoop能够启用压缩的一些配置
在使用sqoop 将数据库表中数据导入至hdfs时 配置启用压缩 hadoop 的命令 检查本地库支持哪些 bin/hadoop checknative 需要配置native 要编译版本 ...
- webpack编译报错:Module not found: Error: Cannot resolve 'file' or 'directory' ./../../node_modules..
在同事的mac电脑上,可以正常编译,拿到我这边就出错了(⊙﹏⊙) 好像是webpack在window下的一个bug,需要让 webpack 和你的项目保持在一个盘符下,参考. 解决方法: 修改conf ...
- react CRA antd 按需加载配置 lessloader
webpack配置 webpack.config.dev.js, webpack.config.prod同理. 'use strict'; const autoprefixer = require(' ...
- 常用的css3新特性总结
1:CSS3阴影 box-shadow的使用和技巧总结: 基本语法是{box-shadow:[inset] x-offset y-offset blur-radius spread-radiuscol ...
- VC改变CListCtrl 表格中文字颜色,和背景颜色。
(1)首先需要自定义一个类,派生自CListCtrl.如下图: (2)然后在派生类的头文件中声明一个成员函数,如下图: (3)在源文件中实现该成员方法,如图: (4)在源文件中做消息映射,如图: 这时 ...
- php常用函数——字符串函数
php常用函数——字符串函数
- MongoDB之主从复制和副本集(四)
简单主从复制 采用一主一从或一主多从的布署模式,可以将读写分离开来,提高数据库的可用性,不过mongodb的主从模式并不能在主节点崩溃后,从节点替换主节点的工作,一般可以在开发阶段使用. 实现步骤 设 ...
- 76.ZYNQ-用PS控制DDR3内存读写
本编文章的目的主要用简明的方法对DDR3进行读写,当然这种方式每次读写都需要CPU干预,效率是比较低的,但是这是学习的过程吧. 本系列文章尽可能的让每一个实验都相对独立,过程尽可能保证完整性,保证实验 ...