8.22 万灵药(SAM + Trie + 树剖 + 线段树)

本文搬运自本人高中时期CSDN博客,若图片加载不出来,可到原文查看:https://blog.csdn.net/zhangtingxiqwq/article/details/141438811

http://cplusoj.com/d/senior/p/479?tid=66c55d60c098fe0f6786d470

考虑如何求两个前缀的最长后缀

我们建一个SAM,把这两个前缀找出来,他们的公共后缀集合为这两个点在fail树上的公共祖先

那么最长后缀就是它们lca对应的最长串

接下来我们要统计这些串的前缀,首先肯定要拿一个trie来维护

直接建trie是 O(n2)O(n^2) 的,但是这里有一个重要的性质,就是我们发现SAM节点和trie树节点是一一对应的

具体的,当前SAM节点在trie树上的父亲必然是某个走到当前节点的节点。我们只需要判断符合条件的len就行

因此我们现在把trie建出来了

我们现在要实现的功能是什么?

对于trie树,我们要查询一个点到根路径上所有值的权值,并对这条路径所有点的权值±1

这个东西直接剖就行

后面维护理论上要拿zkw或者树状数组才行,但我线段树冲过去了,复杂度是双log的

如果改成全局平衡二叉树,那就可以优化成单log了,黄队好像打了

my code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
#include<bits/stdc++.h>
using namespace std;
#ifdef LOCAL
#define debug(...) fprintf(stdout, ##__VA_ARGS__)
#define debag(...) fprintf(stderr, ##__VA_ARGS__)
#else
#define debug(...) void(0)
#define debag(...) void(0)
#endif
#define int long long
inline int read(){int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;
ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+
(x<<3)+(ch^48);ch=getchar();}return x*f;}
#define Z(x) (x)*(x)
#define pb push_back
#define fi first
#define se second
//#define M
//#define mo
#define N 1000010
int n, m, i, j, k, T, ans;

struct Binary_tree {
int cnt[N];
void add(int x, int y) {
if(x == 0) return cnt[x] += y, void();
while(x < N) cnt[x] += y, x += x & -x;
}
int qry(int x) {
int ans = cnt[0];
while(x) ans += cnt[x], x -= x & -x;
return ans;
}
}Bin1, Bin2;

struct Segment_tree {
#define ls (k << 1)
#define rs (k << 1 | 1)
#define mid ((l + r) >> 1)
int tag[N << 2], s[N << 2];
void push_down(int k, int l, int r) {
tag[ls] += tag[k]; tag[rs] += tag[k];
s[ls] += tag[k] * (mid - l + 1);
s[rs] += tag[k] * (r - mid);
assert(s[ls] + s[rs] == s[k]);
tag[k] = 0;
}
void push_up(int k) { s[k] = s[ls] + s[rs]; }
void add(int k, int l, int r, int x, int y, int v) {
if(l >= x && r <= y) return tag[k] += v, s[k] += v * (r - l + 1), void();
push_down(k, l, r);
if(x <= mid) add(ls, l, mid, x, y, v);
if(y >= mid + 1) add(rs, mid + 1, r, x, y, v);
push_up(k);
}
int qry(int k, int l, int r, int x, int y) {
if(l >= x && r <= y) return s[k];
push_down(k, l, r); int ans = 0;
if(x <= mid) ans += qry(ls, l, mid, x, y);
if(y >= mid + 1) ans += qry(rs, mid + 1, r, x, y);
// debug("qry[%lld %lld] is %lld\n", )
return ans;
}
}Seg;

struct Big_Binary_tree {
void add(int x, int v) {
Bin1.add(x, v);
Bin2.add(x, v * x);
}
void add(int l, int r, int v) {
l = max(2ll, l);
debug("[%lld %lld] += %lld\n", l, r, v);
Seg.add(1, 1, 2 * n, l, r, v);
// add(r, v); add(l - 1, -v);
}
int qry(int x) {
int s1 = Bin1.qry(x), s2 = Bin2.qry(x);
return (x + 1) * s1 - s2;
}
int qry(int l, int r) {
l = max(2ll, l);
// debug("qry[%lld %lld] is %lld\n", l, r, Seg.qry(1, 1, 2 * n, l, r));
return Seg.qry(1, 1, 2 * n, l, r);
// return qry(r) - qry(l - 1);
}
}Bin;

struct Trie_tree {
int tot = 0;
int w[N], son[N], f[N], top[N], cnt[N];
int dfn[N];
vector<int>G[N];
void add_edge(int x, int y, int c) { G[x].pb(y); f[y] = x; }
void dfs1(int x) {
w[x] = 1;
for(int y : G[x]) {
dfs1(y); w[x] += w[y];
if(w[y] > w[son[x]]) son[x] = y;
}
}
void dfs2(int x, int Top) {
top[x] = Top; dfn[x] = ++tot;
debug("Pou %lld [%lld]\n", x, Top);
if(son[x]) dfs2(son[x], Top);
for(int y : G[x]) if(y != son[x]) dfs2(y, y);
}
void work() {
dfs1(1); dfs2(1, 1);
}
void calc_add(int x, int v) {
while(x) {
int l = dfn[top[x]], r = dfn[x];
if(v == -1) Bin.add(l, r, v);
ans += Bin.qry(l, r) * v;
if(v == 1) Bin.add(l, r, v);
x = f[top[x]];
}
}
void modify(int x) {
calc_add(x, cnt[x] ? -1 : 1);
cnt[x] ^= 1;
}
}Trie;

struct Sam {
int i, j, k, tot, lst;
int len[N], fail[N], nxt[N][6];
vector<int>G[N];
void init() { tot = lst = 1; }
int ins(int x) {
int u = ++tot, p, q, cq;
p = lst; lst = u;
len[u] = len[p] + 1;
while(p && !nxt[p][x]) nxt[p][x] = u, p = fail[p];
if(!p) return fail[u] = 1, u;
q = nxt[p][x];
if(len[p] + 1 == len[q]) fail[u] = q;
else {
cq = ++tot;
fail[cq] = fail[q]; fail[q] = fail[u] = cq;
len[cq] = len[p] + 1;
memcpy(nxt[cq], nxt[q], sizeof(nxt[q]));
while(p && nxt[p][x] == q) nxt[p][x] = cq, p = fail[p];
}
return u;
}
int dep[N], f[N][22];
void dfs(int x, int fa) {
dep[x] = dep[fa] + 1;
for(int y : G[x]) dfs(y, x);
}
void work() {
for(i = 1; i <= tot; ++i) {
for(j = 1; j <= 4; ++j) {
k = nxt[i][j];
if(k) debug("%lld --%c--> %lld\n", i, (char)(j + 'a' - 1), k);
if(len[i] + 1 == len[k]) {
Trie.add_edge(i, k, j);
debug("%lld --> %lld\n", i, k);
}
}
}
for(i = 1; i <= tot; ++i) debug("%lld ", fail[i]); debug("\n");
for(i = 2; i <= tot; ++i) G[fail[i]].pb(i), f[i][0] = fail[i];
for(k = 1; k <= 20; ++k)
for(i = 1; i <= tot; ++i)
f[i][k] = f[f[i][k - 1]][k - 1];
dfs(1, 0);
}
int lca(int x, int y) {
if(x == y) return x;
if(dep[x] < dep[y]) swap(x, y);
for(int k = 20; k >= 0; --k)
if(dep[f[x][k]] >= dep[y]) x = f[x][k];
if(x == y) return x;
for(int k = 20; k >= 0; --k)
if(f[x][k] != f[y][k]) x = f[x][k], y = f[y][k];
return f[x][0];
}

}SAM;

char s[N];
int point[N];

signed main()
{
#ifdef LOCAL
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
// srand(time(NULL));
// T=read();
// while(T--) {
//
// }
scanf("%s", s + 1); n = strlen(s + 1); SAM.init();
for(i = 1; i <= n; ++i) point[i] = SAM.ins(s[i] - 'a' + 1);
SAM.work(); Trie.work();
int Q = read();
while(Q--) {
int x, y, z;
x = read(); y = read(); z = SAM.lca(point[x], point[y]);
debug("max(%lld %lld) = %lld\n", x, y, z);
Trie.modify(z);
printf("%lld\n", ans);
}
return 0;
}