「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 < ...
随机推荐
- NYOJ 138 找球号(二) (哈希)
题目链接 描述 在某一国度里流行着一种游戏.游戏规则为:现有一堆球中,每个球上都有一个整数编号i(0<=i<=100000000),编号可重复,还有一个空箱子,现在有两种动作:一种是&qu ...
- discuz2.5登录后台闪退的解决办法
今天突然发现discuz2.5论坛后台进不去,开始以为密码错了,但发现登录后也是闪退.我试着清除浏览器cookie,也换了其他浏览器也没有用,还是上网找找吧! discuz2.5进入后台闪退的原因: ...
- 【C++自我精讲】基础系列六 PIMPL模式
[C++自我精讲]基础系列六 PIMPL模式 0 前言 很实用的一种基础模式. 1 PIMPL解释 PIMPL(Private Implementation 或 Pointer to Implemen ...
- 搭建linux+nginx+mysql+php环境
yum install -y gcc gcc-c++ make zlib zlib-devel pcre pcre-devel libjpeg libjpeg-devel libpng libpn ...
- Ubuntu或者Ubuntu server重新设置IP地址
1.打开终端输入: sudo vi /etc/network/interfaces 2.进入编辑页面 改一处,添加5行内容,如下图: 3.修改好后esc :wq进行保存 4.输入: sudo / ...
- 使用常见的网络命令查看当前网络状态——Mac OS X篇
转载自:http://blog.csdn.net/zkh90644/article/details/50539948 操作系统拥有一套通用的实用程序来查明本地主机的有线或者无线链路状态和IP的连接情况 ...
- 制作自动化系统安装U盘
1.挂载CentOS6.6镜像 [root@test88 ~]# mkdir /application/tools -p [root@test88 ~]# cd /application/tools/ ...
- 你需要知道的Nginx配置二三事
做服务端开发的,工作中难免会遇到处理Nginx配置相关问题.在配置Nginx时,我一直本着“照葫芦画瓢”的原则,复制已有的配置代码,自己修修改改然后完成配置需求,当有人问起Nginx相关问题时,其实仍 ...
- web.xml中的dispatchservlet后,js,css,甚至gif都不能正常显示
这个可以说是很多初学Springmvc的人都会碰到一个令人头痛的问题 那就是为什么我配置好web.xml中的dispatchservlet后,js,css,甚至gif都不能正常显示了 我们来看看我们配 ...
- 画弧线DrawArc的研究-我自己 -- 直线交接圆角
procedure TForm4.Button7Click(Sender: TObject); var pwith: Integer; //画笔的宽度 hx1, hy1: Integer; //横线第 ...