【loj - 3055】「HNOI2019」JOJO
description
JOJO 的奇幻冒险是一部非常火的漫画。漫画中的男主角经常喜欢连续喊很多的「欧拉」或者「木大」。
为了防止字太多挡住漫画内容,现在打算在新的漫画中用 \(x\) 欧拉或者 \(x\) 木大表示有 \(x\) 个欧拉或者木大。
为了简化内容我们现在用字母表示喊出的话。
我们用数字和字母来表示一个串,例如:2 a 3 b
表示的串就是 aabbb
。
一开始漫画中什么话都没有,接下来你需要依次实现 \(n\) 个操作,总共只有 \(2\) 种操作:
- 第一种:
1 x c
:在当前漫画中加入 \(x\) 个 \(c\),表示在当前串末尾加入 \(x\) 个 \(c\) 字符。保证当前串是空串或者串尾字符不是 \(c\); - 第二种:
2 x
:觉得漫画没画好,将漫画还原到第 \(x\) 次操作以后的样子,表示将串复原到第 \(x\) 次操作后的样子,如果 \(x = 0\) 则是将串变成空串。如果当前串是bbaabbb
,第 \(4\) 次操作后串是bb
,则2 4
会使bbaabbb
变成bb
,保证 \(x\) 小于当前操作数。
众所周知空条承太郎十分聪明,现在迪奥已经被打败了,他开始考虑自己的漫画中的一些问题:
对于一个串的每个前缀 \(A\),都有一个最长的比它短的前缀 \(B\) 与前缀 \(A\) 的一个后缀匹配,设这个最长的前缀 \(B\) 的长度为 \(L\)。\(L\) 为 \(0\) 时意味着 \(B\) 是一个空串。
每一次操作后,你都需要将当前的串的所有前缀的 \(L\) 求和并对 \(998244353\) 取模输出告诉空条承太郎,好和他的白金之星算出的答案对比。比如 bbaaabba
的 \(L\) 分别是 \(0,1,0,0,0,1,2,3\),所以对于这个串的答案就是 \(7\)。
solution
假如存在 s[1...x] = s[n-x+1...n],则中间完整的块必须字符与个数一一对应相等。
由此,我们不妨把某次操作增加的 (x, c) 视作二元组,对二元组做 kmp 求最大 border。
不过这里要特判一下第一个块:后半部分的第一个块长度 ≥ 前半部分第一个块(就是整个串第一个块)长度,且它们的字符相同,则认为它们相等。
可以发现这样操作依然满足 border 的传递性,实现时只在需要判相等时这样搞一下。
至于求答案。如果是整个串第一个块特判;否则边跳 fail 边记录当前已经匹配的最大值(需要注意即使存在长度 > 新加入的串长度也不能停止跳 fail,必须严格等于)。
还要特判上文所述 “后半部分的第一个块长度 ≥ 前半部分第一个块” 情况。
不过题目中还有 2 操作,离线下来发现就是 trie 上求最大 border。
然而 kmp 的复杂度是均摊的(虽然这道题你暴力跳好像也能过),我们考虑改一改求最大 border 的方法。
一种方法是建 O(∑)(字符集大小)个可持久化线段树,维护加入某一类字符共 k 个所转移到的点以及贡献。每次加入新点只会修改 O(1) 棵线段树。
另一种是利用 border 与循环的性质(形成 O(log) 个等差数列),每次如果当前循环节大小与上一次循环节大小相等,就可以直接对循环节取模。
accepted code
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
const int MAXN = 100000;
const int MOD = 998244353;
inline int add(int x, int y) {x += y; return x >= MOD ? x - MOD : x;}
inline int sub(int x, int y) {x -= y; return x < 0 ? x + MOD : x;}
inline int mul(int x, int y) {return (int) (1LL * x * y % MOD);}
struct type{
int ch, cnt;
friend bool operator == (const type &a, const type &b) {
return (a.ch == b.ch) && (a.cnt == b.cnt);
}
}a[MAXN + 5];
vector<int>ch[MAXN + 5]; int ncnt;
int add(int x, type t) {
a[++ncnt] = t, ch[x].push_back(ncnt);
return ncnt;
}
type stk[MAXN + 5]; int tp;
void update(int &res, int &lens, int k, int lim) {
if( lim > lens ) {
res = add(res, mul(sub(lim, lens), k));
res = add(res, (int)(1LL*(lens + 1 + lim)*(lim - lens)/2%MOD));
lens = lim;
}
}
int res[MAXN + 5], len[MAXN + 5], fail[MAXN + 5];
void insert(int x, type t) {
int nw = fail[tp], lens = 0, lst = -1;
stk[++tp] = t, len[tp] = len[tp - 1] + t.cnt, res[x] = 0;
while( nw != -1 ) {
if( stk[nw + 1].ch == t.ch ) {
update(res[x], lens, len[nw], min(stk[nw + 1].cnt, t.cnt));
if( nw == 0 && stk[1].cnt < t.cnt ) {
res[x] = add(res[x], mul(t.cnt - lens, stk[1].cnt));
break;
} else if( stk[nw + 1].cnt == t.cnt )
break;
}
if( nw && nw - fail[nw] == lst && 2*lst < nw )
nw %= lst;
else nw = fail[nw];
}
fail[tp] = nw + 1;
if( tp == 1 )
res[x] = add(res[x], (int)(1LL*(t.cnt - 1)*t.cnt/2%MOD));
}
void dfs(int x, int ans) {
insert(x, a[x]), res[x] = add(res[x], ans);
for(unsigned i=0;i<ch[x].size();i++)
dfs(ch[x][i], res[x]);
tp--;
}
int id[MAXN + 5];
int main() {
int n; scanf("%d", &n);
for(int i=1,op,x;i<=n;i++) {
scanf("%d%d", &op, &x);
if( op == 1 ) {
char str[2]; scanf("%s", str);
id[i] = add(id[i - 1], (type){str[0] - 'a', x});
} else id[i] = id[x];
}
fail[0] = -1;
for(unsigned i=0;i<ch[0].size();i++) dfs(ch[0][i], 0);
for(int i=1;i<=n;i++) printf("%d\n", res[id[i]]);
}
details
对于第二种方法,注意不能当循环节长度 < len/2 时就直接模循环节。
感性解释一下:因为我所要匹配的是 fail + 1 的位置,而循环节只能保证 [1...fail] 的有循环节。
【loj - 3055】「HNOI2019」JOJO的更多相关文章
- 【loj - 3056】 「HNOI2019」多边形
目录 description solution accepted code details description 小 R 与小 W 在玩游戏. 他们有一个边数为 \(n\) 的凸多边形,其顶点沿逆时 ...
- 【LOJ 2004】「SDOI2017」硬币游戏
LOJ 2004 100pts 首先我们肯定要建AC自动机的.. 那么这题就肯定是个AC自动机上\(dp\). 所以想想状态. 首先如果我们把状态设成这样行不行: \(dp(i)\)表示匹配到了i节点 ...
- 【LOJ 2145】「SHOI2017」分手是祝愿
LOJ 2145 100pts 这题...BT啊 首先我们很容易想出\(dp(msk)\)表示现在灯开关的情况是\(msk\),期望通过多少步走到终结态. 很明显\(dp(msk)=\frac{1}{ ...
- 【LOJ 2144】「SHOI2017」摧毁「树状图」
LOJ 2144 84pts 首先\(op2\)很简单.直接并查集一搞就好了(话说我现在什么东西都要写个并查集有点...) 然后\(op0\)我不会,就直接\(O(n^2)\)枚举一下\(P\)这个人 ...
- Loj #3055. 「HNOI2019」JOJO
Loj #3055. 「HNOI2019」JOJO JOJO 的奇幻冒险是一部非常火的漫画.漫画中的男主角经常喜欢连续喊很多的「欧拉」或者「木大」. 为了防止字太多挡住漫画内容,现在打算在新的漫画中用 ...
- 【LOJ#6066】「2017 山东一轮集训 Day3」第二题(哈希,二分)
[LOJ#6066]「2017 山东一轮集训 Day3」第二题(哈希,二分) 题面 LOJ 题解 要哈希是很显然的,那么就考虑哈希什么... 要找一个东西可以表示一棵树,所以我们找到了括号序列. 那么 ...
- 一本通1648【例 1】「NOIP2011」计算系数
1648: [例 1]「NOIP2011」计算系数 时间限制: 1000 ms 内存限制: 524288 KB [题目描述] 给定一个多项式 (ax+by)k ,请求出多项式展开后 x ...
- 【LOJ 6041】「雅礼集训 2017 Day7」事情的相似度
Description 人的一生不仅要靠自我奋斗,还要考虑到历史的行程. 历史的行程可以抽象成一个 01 串,作为一个年纪比较大的人,你希望从历史的行程中获得一些姿势. 你发现在历史的不同时刻,不断的 ...
- 【LOJ#536】「LibreOJ Round #6」花札
题目链接 题目描述 「UniversalNO」的规则如下:每张牌有一种颜色和一个点数.两个人轮流出牌,由 Alice 先手,最开始牌堆为空,出的人可以出任意牌(放到牌堆顶),之后出的牌必须和当时牌堆顶 ...
随机推荐
- MySQL(9)— 规范数据库设计
九.规范数据库设计 9-1.为什么要设计? 当数据库比较复杂时,我们就需要设计了! 糟糕的数据库设计: 数据冗余,浪费大量存储空间 使用物理外键,大量的增删改操作麻烦,异常 查询效率低下 良好的数据库 ...
- PHP实现插入100万条数据优化
第一种方法一条一条执行插入,结果会很慢 <?php header("Content-Type:text/html;charset=utf-8"); date_default_ ...
- 解释一下,@SpringBootApplication
解释一下,@SpringBootApplication其实就是以下三个注解的总和 @Configuration: 用于定义一个配置类 @EnableAutoConfiguration :Spring ...
- day24 面向对象与实例属性
编程进化论: 1.编程最开始就是无组织无结构,从简单控制流中按步写指令 2.从上述的指令中提取重复的代码块或逻辑,组织到一起(比方说,你定义了一个函数),便实现来代码重用,且代码从无结构走向了机构化, ...
- 微服务项目的docker自动化部署流程
目录 微服务的Docker自动化部署 制作JDK1.8的Docker镜像 Docker常用命令介绍 制作image的一般流程 将本地的image上传至私人仓库 使用Maven插件实现自动化docker ...
- AES128_CBC模式加密
高级加密标准(英语:Advanced Encryption Standard,缩写:AES),在密码学中又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准.这个标准用来替代原先的DES, ...
- Java集合(九)哈希冲突及解决哈希冲突的4种方式
Java集合(九)哈希冲突及解决哈希冲突的4种方式 一.哈希冲突 (一).产生的原因 哈希是通过对数据进行再压缩,提高效率的一种解决方法.但由于通过哈希函数产生的哈希值是有限的,而数据可能比较多,导致 ...
- Unity 离线建造系统
很多游戏,特别是养成类手游,都会有自己独特的建造系统,一个建造装置的状态循环或者说生命周期一般是这样的: 1.准备建造,设置各项资源的投入等 2.等待一段倒计时,正在建造中 3.建造结束,选择是否收取 ...
- 泛微 e-cology远程代码执行漏洞
影响版本:泛微 e-cology<=9.0 漏洞分析: 问题出现在 resin 下 lib 中的 bsh.jar 文件里,问题类 bsh.servlet.BshServlet,可 doGet 方 ...
- 50个SQL语句(MySQL版) 问题九
--------------------------表结构-------------------------- student(StuId,StuName,StuAge,StuSex) 学生表 tea ...