• 【loj



    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];
    		lst = 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
    ", res[id[i]]);
    }
    

    details

    对于第二种方法,注意不能当循环节长度 < len/2 时就直接模循环节。
    感性解释一下:因为我所要匹配的是 fail + 1 的位置,而循环节只能保证 [1...fail] 的有循环节。

  • 相关阅读:
    使用 v-cloak 防止页面加载时出现 vuejs 的变量名
    Jackson 解析json数据之忽略解析字段注解@JsonIgnoreProperties
    java.lang.IllegalStateException: Cannot run without an instance id.
    沪牌-上海牌照-拍牌经验分享: 我是如何三次拍中的?
    沪牌学院-沪拍拍课堂4: 实拍前的演练
    沪牌学院-沪拍拍课堂3: 网络优化
    沪牌学院-沪拍拍课堂2: 出价策略
    沪牌学院-沪拍拍课堂1: 估价策略
    如何将 DVD 转成 ISO
    雅虎天气-城市代码列表
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/13094752.html
Copyright © 2020-2023  润新知