【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 先手,最开始牌堆为空,出的人可以出任意牌(放到牌堆顶),之后出的牌必须和当时牌堆顶 ...
随机推荐
- django 中CBV和FBV 路由写法的区别
使用视图函数时, FBV: django完成URL解析之后,会直接把request对象以及URL解析器捕获的参数(比如re_path中正则表达捕获的位置参数或关键字参数)丢给视图函数 CBV: 这些参 ...
- mysql 查询获取排名的方法(绝对有效)
http://blog.csdn.net/k8080880/article/details/11253305 select case when pid=0 then case when @prevTy ...
- Identity Card(hdu2629)
输入方式:先输入一个整型,再输入不带空格未知长度/已知长度的字符串. 思考:用scanf_s()函数输入整型,再循环输入不带空格未知长度的字符串,用gets_s()函数. 注意:scanf_s()函数 ...
- ShoneSharp语言(S#)的设计和使用介绍系列(9)— 一等公民“函数“爱炫巧
ShoneSharp语言(S#)的设计和使用介绍 系列(9)— 一等公民“函数“爱炫巧 作者:Shone 声明:原创文章欢迎转载,但请注明出处,https://www.cnblogs.com/Shon ...
- tp入门
其中可以配置隐藏看入口文件 和默认读取路径 <?php namespace app\admin\controller; //生命控制器 class Index { public function ...
- [COCOS2DX-LUA]0-005.cocos2dx中关于全面屏和折叠屏的适配的一些见解
1.随着科技的发展,我们可以看到从iphoneX的刘海屏开始,引发了各种全面屏和异形屏的出现.这是科技的进步,但是对于各大的应用厂商来说,苦不堪言. 2.当然 ,吐槽归吐槽,我们还是要理智的去对待这个 ...
- [JavaWeb基础] 005.Hibernate的配置和简单使用
1.Hibernate简介 Hibernate是一个基于Java的开源的持久化中间件,对JDBC做了轻量的封装.采用ORM映射机制,负责实现Java对象和关系数据库之间的映射,把sql语句传给数据库, ...
- pandas手册
https://www.dataquest.io/blog/large_files/pandas-cheat-sheet.pdf
- DQN(Deep Q-learning)入门教程(四)之Q-learning Play Flappy Bird
在上一篇博客中,我们详细的对Q-learning的算法流程进行了介绍.同时我们使用了\(\epsilon-贪婪法\)防止陷入局部最优. 那么我们可以想一下,最后我们得到的结果是什么样的呢?因为我们考虑 ...
- echarts实现漏斗转化率图表效果
1.在用echarts实现图表的旅途中遇到这样一个需求,用柱图展示漏斗转化效果,下图展示: 别的不多说了,就说解决方式吧,用的series中的markpoint来实现. option.series[0 ...