【题目链接】
- 点击打开链接
【思路要点】
- 考虑将字符串缩成若干个相同字符的段 ( c n t , c h a r ) (cnt,char) (cnt,char) ,相邻的字符不相同。
- 考虑一个与某一个前缀匹配的后缀,若该后缀横跨了 x ( x ≥ 2 ) x\ (x\geq2) x (x≥2) 段,那么其对应的第 2 2 2 段至第 x − 1 x-1 x−1 段的 ( c n t , c h a r ) (cnt,char) (cnt,char) 都应该对应相等;且后缀的第 1 1 1 段与前缀的第 1 1 1 段的 c h a r char char 相等,后缀的 c n t cnt cnt 不小于前缀的 c n t cnt cnt ;后缀的第 x x x 段与前缀的第 x x x 段的 c h a r char char 相等,前缀的 c n t cnt cnt 不小于后缀的 c n t cnt cnt 。
- 离线将字符树建出来,然后求出各个点的 f a i l / n e x t fail/next fail/next ,注意这里的匹配允许当前匹配的第 1 1 1 段的 c n t cnt cnt 不小于前缀第 1 1 1 段的 c n t cnt cnt ,具体实现时可以使用可持久化线段树实现回退。
- 接下来求解答案,我们需要计算新产生的前缀 n e w new new 对答案的贡献。当前节点 c u r cur cur 的 f a i l fail fail 树上的每一个节点 f a fa fa 都存在后继字符 c h a r s o n f a char_{son_{fa}} charsonfa 及其长度 c n t s o n f a cnt_{son_{fa}} cntsonfa ,若 c h a r s o n f a = c h a r n e w char_{son_{fa}}=char_{new} charsonfa=charnew ,则对于每一个 1 ≤ i ≤ M i n { c n t s o n f a , c n t n e w } 1\leq i\leq Min\{cnt_{son_{fa}},cnt_{new}\} 1≤i≤Min{cntsonfa,cntnew} ,新产生的第 i i i 个前缀将存在一个长度为 d e p t h f a + i depth_{fa}+i depthfa+i 的匹配。且由于 d e p t h f a depth_{fa} depthfa 是递增的,可以将其看做一次区间赋值操作,用可持久化线段树维护各个字符的各个长度的匹配以实现回退。
- 注意特殊处理匹配没有横跨 2 2 2 段的情况。
- 时间复杂度 O ( N L o g N ) O(NLogN) O(NLogN) 。
【代码】
#include
using namespace std; const int MAXN = 2e5 + 5; const int MAXP = 5e6 + 5; typedef long long ll; typedef long double ld; typedef unsigned long long ull; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } struct SegmentTreeWithInfo { struct Node { int lc, rc; int val; } a[MAXP]; int root, size, n; void init(int x) { n = x; root = size = 0; } int modify(int root, int l, int r, int pos, int d) { int ans = ++size; a[ans] = a[root]; if (l == r) { a[ans].val = d; return ans; } int mid = (l + r) / 2; if (mid >= pos) a[ans].lc = modify(a[root].lc, l, mid, pos, d); else a[ans].rc = modify(a[root].rc, mid + 1, r, pos, d); return ans; } int modify(int root, int pos, int d) { return modify(root, 1, n, pos, d); } int query(int root, int l, int r, int pos) { if (l == r) return a[root].val; int mid = (l + r) / 2; if (mid >= pos) return query(a[root].lc, l, mid, pos); else return query(a[root].rc, mid + 1, r, pos); } int query(int root, int pos) { return query(root, 1, n, pos); } } Edge; struct SegmentTreeWithAnswer { struct Node { int lc, rc; int tag; ll sum; } a[MAXP]; int root, size, n; void init(int x) { n = x; root = size = 0; } void update(int root) { a[root].sum = a[a[root].lc].sum + a[a[root].rc].sum; } void pushdown(int root, int l, int r) { if (a[root].tag) { int mid = (l + r) / 2; int lc = ++size; a[lc] = a[a[root].lc]; a[lc].tag = a[root].tag; a[lc].sum = 1ll * (mid - l + 1) * a[root].tag; a[root].lc = lc; int rc = ++size; a[rc] = a[a[root].rc]; a[rc].tag = a[root].tag; a[rc].sum = 1ll * (r - mid) * a[root].tag; a[root].rc = rc; a[root].tag = 0; } } int modify(int root, int l, int r, int ql, int qr, int d) { int ans = ++size; a[ans] = a[root]; if (l == ql && r == qr) { a[ans].tag = d; a[ans].sum = (r - l + 1ll) * d; return ans; } pushdown(ans, l, r); int mid = (l + r) / 2; if (mid >= ql) a[ans].lc = modify(a[ans].lc, l, mid, ql, min(mid, qr), d); if (mid + 1 <= qr) a[ans].rc = modify(a[ans].rc, mid + 1, r, max(mid + 1, ql), qr, d); update(ans); return ans; } int modify(int root, int pos, int d) { return modify(root, 1, n, 1, pos, d + 1); } int getMax(int root, int l, int r) { if (a[root].tag) return r; int mid = (l + r) / 2; if (a[a[root].rc].sum) return getMax(a[root].rc, mid + 1, r); else return getMax(a[root].lc, l, mid); } ll getsum(int root, int l, int r, int ql, int qr) { if (l == ql && r == qr) return a[root].sum; if (a[root].tag) return (qr - ql + 1ll) * a[root].tag; int mid = (l + r) / 2; ll ans = 0; if (mid >= ql) ans += getsum(a[root].lc, l, mid, ql, min(qr, mid)); if (mid + 1 <= qr) ans += getsum(a[root].rc, mid + 1, r, max(mid + 1, ql), qr); return ans; } ll query(int root, int pos, int least) { if (a[root].sum == 0) return 0; int Max = getMax(root, 1, n); ll ans = 0; if (Max < pos) { if (least <= Max) ans = 1ll * (pos - Max) * least; else if (least >= pos) ans = (Max + 1ll + pos) * (pos - Max) / 2; else ans = 1ll * least * (least - Max) + (least + 1ll + pos) * (pos - least) / 2; } chkmin(pos, Max); return ans + getsum(root, 1, n, 1, pos) + pos * (pos - 1ll) / 2; } } ST; struct AcAutomaton { struct Node { vector <int> sons, ori; int depth, redge, rans, fail; char c; ll ans; } a[MAXN]; char key; int first, firstp; map <pair <int, char>, int> home; int cnte, Max, size, tot, pos[MAXN]; void update(pair <int, char> x) { if (!home.count(x)) home[x] = ++cnte; } void rebuild(int pos, char c, int fa) { for (auto x : a[pos].ori) { if (a[x].c == c) { a[fa].sons.push_back(x); update(make_pair(a[x].depth - a[fa].depth, a[x].c)); rebuild(x, a[x].c, fa); } else { a[pos].sons.push_back(x); update(make_pair(a[x].depth - a[pos].depth, a[x].c)); rebuild(x, a[x].c, pos); } } } void getfail(int pos) { //cerr << pos << ' ' << a[pos].fail << endl; for (auto x : a[pos].sons) { chkmax(Max, a[x].depth - a[pos].depth); a[x].fail = Edge.query(a[a[pos].fail].redge, home[make_pair(a[x].depth - a[pos].depth, a[x].c)]); if (a[x].fail == 0 && a[x].c == key && a[x].depth - a[pos].depth >= first) a[x].fail = firstp; a[x].redge = a[a[x].fail].redge; int bak = a[pos].redge; a[pos].redge = Edge.modify(a[pos].redge, home[make_pair(a[x].depth - a[pos].depth, a[x].c)], x); if (pos == 0) key = a[x].c, first = a[x].depth - a[pos].depth, firstp = x; getfail(x); a[pos].redge = bak; } } ll func(int x) { return x * (x - 1ll) / 2; } void getans(int pos) { for (auto x : a[pos].sons) { int troot = Edge.query(a[a[pos].fail].redge, a[x].c - 'a' + 1); if (pos == 0) { a[x].ans = a[pos].ans + func(a[x].depth - a[pos].depth); key = a[x].c, first = a[x].depth - a[pos].depth; } else a[x].ans = a[pos].ans + ST.query(troot, a[x].depth - a[pos].depth, first * (a[x].c == key)); a[x].redge = a[a[x].fail].redge; int bak = a[pos].redge; troot = Edge.query(a[pos].redge, a[x].c - 'a' + 1); troot = ST.modify(troot, a[x].depth - a[pos].depth, a[pos].depth); a[pos].redge = Edge.modify(a[pos].redge, a[x].c - 'a' + 1, troot); getans(x); a[pos].redge = bak; } } void work() { rebuild(0, -1, 0); Edge.init(cnte); getfail(0); Edge.init(26); ST.init(Max); getans(0); for (int i = 1; i <= tot; i++) writeln(a[pos[i]].ans % 998244353); } void extend(int x, char c) { pos[++tot] = ++size; a[size].c = c; a[size].depth = a[pos[tot - 1]].depth + x; a[pos[tot - 1]].ori.push_back(size); } void rev(int x) { pos[++tot] = pos[x]; } } ACAM; int main() { int n; read(n); for (int i = 1; i <= n; i++) { int opt, x; char c; read(opt), read(x); if (opt == 1) { c = getchar(); ACAM.extend(x, c); } else ACAM.rev(x); } ACAM.work(); return 0; }