BZOJ传送门
题目描述
懒得写背景了,给你一个字符串init,要求你支持两个操作
(1):在当前字符串的后面插入一个字符串
(2):询问字符串 s s s在当前字符串中出现了几次?(作为连续子串)
你必须在线支持这些操作。
输入输出格式
输入格式:
两行,两个字符串 s 1 s_1 s1, s 2 s_2 s2,长度分别为 n 1 n_1 n1, n 2 n_2 n2。 1 ≤ n 1 , n 2 ≤ 200000 1 \le n_1, n_2\le 200000 1≤n1,n2≤200000,字符串中只有小写字母
输出格式:
输出一个整数表示答案
输入输出样例
输入样例#1:
2
A
QUERY B
ADD BBABBBBAAB
输出样例#1:
0
解题分析
这道题就是要我们动态维护后缀自动机的 r i g h t right right集合大小, 显然每次构造后暴力向上跳是不科学的, 而我们发现添加一个字符等于在 p a r e n t parent parent树上面到根节点的路径上整体加 1 1 1。 又因为涉及到 p a r e n t parent parent链的断开、动态连接操作, 我们考虑用 L C T LCT LCT维护 p a r e n t parent parent树,在我们更改 p a r e n t parent parent数组的时候 l i n k link link和 c u t cut cut。
因为是有根树, 所以不能 s p l i t split split, 我们区间加直接 a c c e s s access access后再 s p l a y splay splay, 进而在 p u s h d o w n pushdown pushdown的时候将标记下传。
还有一个坑点:一定要单独开一个数组保存每个节点的 p a r e n t parent parent!因为 L C T LCT LCT形态不固定, 会把真正的 p a r e n t parent parent s p l a y splay splay到奇奇怪怪的地方。
总复杂度 O ( N l o g ( N ) ) O(Nlog(N)) O(Nlog(N)), 600000 600000 600000的 L C T LCT LCT跑的还贼快?
代码如下:
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <cctype>
#include <algorithm>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define MX 1500050
char dat[MX], opt[10];
int l, cnt = 1, top, cur, last = 1, root = 1, key, lastans, sta[MX], fat[MX];
struct Node {int son[2], fat, tag, to[26], len, siz;} tree[MX];
namespace LCT
{
#define ls tree[now].son[0]
#define rs tree[now].son[1]
#define dad tree[now].fat
IN bool get(R int now) {return tree[dad].son[1] == now;}
IN bool nroot(R int now) {return tree[dad].son[0] == now || tree[dad].son[1] == now;}
IN void add(R int now, R int val) {if(now) tree[now].siz += val, tree[now].tag += val;}
IN void pushdown(R int now)
{if(tree[now].tag) add(ls, tree[now].tag), add(rs, tree[now].tag), tree[now].tag = 0;}
IN void rotate(const int &now)
{
R bool dir = get(now);
R int fa = dad, grand = tree[fa].fat;
tree[fa].son[dir] = tree[now].son[dir ^ 1];
tree[tree[now].son[dir ^ 1]].fat = fa;
if(nroot(fa)) tree[grand].son[get(fa)] = now;
tree[now].fat = grand;
tree[now].son[dir ^ 1] = fa;
tree[fa].fat = now;
}
IN void splay(R int now)
{
int tmp = now, fa;
sta[top = 1] = now;
W (nroot(now)) sta[++top] = now = dad;
W (top) pushdown(sta[top--]);
now = tmp;
W (nroot(now))
{
fa = dad;
if(nroot(fa)) rotate(get(fa) == get(now) ? fa : now);
rotate(now);
}
}
IN void access(R int now)
{
for (R int x = 0; now; x = now, now = dad)
splay(now), rs = x;
}
IN void link(R int x, R int y)
{
tree[x].fat = y;
access(y), splay(y);
add(y, tree[x].siz);
}
IN void cut(R int now)
{
access(now); splay(now);
add(ls, -tree[now].siz);
ls = tree[ls].fat = 0;
}
}
namespace SAM
{
IN void insert(R int id)
{
R int now = last, tar;
cur = ++cnt; tree[cur].siz = 1; tree[cur].len = tree[last].len + 1; last = cur;
for (; now && !tree[now].to[id]; now = fat[now])
tree[now].to[id] = cur;
if(!now) return fat[cur] = root, LCT::link(cur, root), void(); tar = tree[now].to[id];
if(tree[tar].len == tree[now].len + 1) return fat[cur] = tar, LCT::link(cur, tar), void();
int nw = ++cnt; tree[nw].len = tree[now].len + 1;
std::memcpy(tree[nw].to, tree[tar].to, sizeof(tree[nw].to));
fat[nw] = fat[tar], LCT::link(nw, fat[tar]); LCT::cut(tar);
fat[cur] = nw, LCT::link(cur, nw); fat[tar] = nw, LCT::link(tar, nw);
for (; now && tree[now].to[id] == tar; now = fat[now]) tree[now].to[id] = nw;
}
IN void init(R int key)
{
for (R int i = 0; i < l; ++i)
{
key = (key * 131 + i) % l;
std::swap(dat[i], dat[key]);
}
}
}
int main(void)
{
int T;
scanf("%d%s", &T, dat);
l = std::strlen(dat);
for (R int i = 0; i < l; ++i) SAM::insert(dat[i] - 'A');
W (T--)
{
scanf("%s%s", opt, dat);
l = std::strlen(dat);
SAM::init(key);
if(opt[0] == 'A')
for (R int i = 0; i < l; ++i)
SAM::insert(dat[i] - 'A');
else
{
R int now = 1;
for (R int i = 0; i < l; ++i) now = tree[now].to[dat[i] - 'A'];
if(!now) puts("0");
else
{
LCT::access(now);
LCT::splay(now);
printf("%d\n", lastans = tree[now].siz);
key ^= lastans;
}
}
}
}