1 条题解
-
0
自动搬运
来自洛谷,原作者为

Alex_Wei
**搬运于
2025-08-24 22:44:01,当前版本为作者最后更新于2023-02-12 13:37:40,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
因为查询和所有询问串相关,所以在加入询问串时,它对每个模板串的贡献都要考虑到。因此只能对所有模板串统一建出结构。
因为查询和模板串子串相关,所以这个结构需要描述所有模板串的所有子串。我们考虑广义 SAM。
加入询问串 ,考虑它的贡献。我们将 输入 SAM,得到它的每个前缀的最长的等于某个模板串子串的后缀长度 ,以及对应状态 。根据 SAM 的 link 树的性质,可知 对某子串产生贡献的充要条件是:
- 存在 在该子串对应状态的子树内(不包括自身)。
- 存在 等于该子串的对应状态,且子串长度不大于对应的 。
相同的 只保留 最大的一个,然后删去所有子树内还有其它 的 。接下来,将所有 的父亲到根的链并上所有节点的答案加 ,表示对应状态的所有子串答案均加 ,链加单点查转成单点加子树查。最后将所有 的所有长度不大于 的子串答案加 ,动态开点线段树维护即可。
说白了,就是把 Divljak 放在广义 SAM 上。
时空复杂度线性对数加线性字符集。
#include <bits/stdc++.h> using namespace std; #define fi first #define se second constexpr int N = 1e6 + 5; constexpr int M = N << 4; constexpr int K = 20; constexpr int S = 26; namespace GSAM { int node, cnt = 1; int son[N][S], len[N], fa[N], anc[K][N]; int ins(int p, int it) { int to = son[p][it]; if(to && len[p] + 1 == len[to]) return to; int cur = ++cnt; len[cur] = len[p] + 1; while(!son[p][it]) son[p][it] = cur, p = fa[p]; if(!p) return fa[cur] = 1, cur; int q = son[p][it]; if(len[p] + 1 == len[q]) return fa[cur] = q, cur; int cl = ++cnt; len[cl] = len[p] + 1; memcpy(son[cl], son[q], S << 2); fa[cl] = fa[q], fa[q] = fa[cur] = cl; while(son[p][it] == q) son[p][it] = cl, p = fa[p]; return to ? cl : cur; } void build() { for(int i = 0; i < K; i++) for(int j = 1; j <= cnt; j++) { if(i) anc[i][j] = anc[i - 1][anc[i - 1][j]]; else anc[i][j] = fa[j]; } } int locate(int pos, int L) { for(int i = K - 1; ~i; i--) { int q = anc[i][pos]; if(len[q] >= L) pos = q; } return pos; } void path(char *s, int m, auto P) { int L = 0, p = 1; for(int i = 1; i <= m; i++) { int it = s[i] - 'a'; while(p > 1 && !son[p][it]) L = len[p = fa[p]]; if(son[p][it]) L++, p = son[p][it]; P[i - 1] = {p, L}; } } } namespace BIT { int c[N]; void add(int x, int v) { while(x < N) c[x] += v, x += x & -x; } int query(int x) { int s = 0; while(x) s += c[x], x -= x & -x; return s; } int query(int l, int r) { return query(r) - query(l - 1); } } namespace SEG { int node, R[N], ls[M], rs[M], val[M]; void modify(int l, int r, int p, int &x) { if(!x) x = ++node; val[x]++; if(l == r) return; int m = l + r >> 1; if(p <= m) modify(l, m, p, ls[x]); else modify(m + 1, r, p, rs[x]); } int query(int l, int r, int ql, int qr, int x) { if(ql <= l && r <= qr || !x) return val[x]; int m = l + r >> 1, ans = 0; if(ql <= m) ans = query(l, m, ql, qr, ls[x]); if(m < qr) ans += query(m + 1, r, ql, qr, rs[x]); return ans; } } using GSAM::fa; using GSAM::len; using GSAM::cnt; int n, q, lg[N], w0, A, B, C = 1; string str[N]; vector<int> pos[N], e[N]; int dn, dfn[N], dep[N], sz[N], mi[K][N]; void dfs(int id) { dfn[id] = ++dn, sz[id] = 1; mi[0][dn] = fa[id], dep[id] = dep[fa[id]] + 1; for(int it : e[id]) dfs(it), sz[id] += sz[it]; } bool isa(int u, int v) { return dfn[u] <= dfn[v] && dfn[v] < dfn[u] + sz[u]; } int get(int x, int y) { return dep[x] < dep[y] ? x : y; } int lca(int u, int v) { if(u == v) return u; if((u = dfn[u]) > (v = dfn[v])) swap(u, v); int d = lg[v - u++]; return get(mi[d][u], mi[d][v - (1 << d) + 1]); } int main() { #ifdef ALEX_WEI freopen("1.in", "r", stdin); freopen("1.out", "w", stdout); #endif ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); cin >> n >> q >> w0; if(w0) cin >> A >> B >> C; for(int i = 1, lst = 1; i <= n; i++, lst = 1) { cin >> str[i], pos[i].resize(str[i].size()); for(int j = 0; j < str[i].size(); j++) { lst = pos[i][j] = GSAM::ins(lst, str[i][j] - 'a'); } } GSAM::build(); for(int i = 2; i <= cnt; i++) e[fa[i]].push_back(i); for(int i = 2; i <= cnt; i++) lg[i] = lg[i >> 1] + 1; dfs(1), assert(cnt == dn); for(int i = 1; i <= lg[cnt]; i++) { for(int j = 1; j + (1 << i) - 1 <= cnt; j++) { mi[i][j] = get(mi[i - 1][j], mi[i - 1][j + (1 << i - 1)]); } } for(int _ = 1, lst = 0; _ <= q; _++) { int op, x = (1ll * A * lst + B) % C; cin >> op; if(op == 1) { static char s[N]; cin >> s + 1; int m = strlen(s + 1); if(w0) { for(int i = 1; i <= m; i++) { swap(s[i], s[x % m + 1]); x = (1ll * A * x + B) % C; } } static pair<int, int> P[N], Q[N]; GSAM::path(s, m, P); sort(P, P + m, [&](auto a, auto b) { return a.fi != b.fi ? dfn[a.fi] < dfn[b.fi] : a.se < b.se; }); int c = 0; for(int i = 0; i < m; i++) { while(c && isa(Q[c - 1].fi, P[i].fi)) c--; Q[c++] = P[i]; } if(c == 1 && Q[0].fi == 1) continue; for(int i = 0; i < c; i++) { int id = Q[i].fi; BIT::add(dfn[fa[id]], 1); if(i) BIT::add(dfn[lca(fa[id], Q[i - 1].fi)], -1); SEG::modify(len[fa[id]] + 1, len[id], Q[i].se, SEG::R[id]); } } else { int p, l, r; cin >> p >> l >> r; if(w0) { p = (p + x) % n + 1; x = (1ll * A * x + B) % C; l = (l + x) % str[p].size() + 1; x = (1ll * A * x + B) % C; r = (r + x) % str[p].size() + 1; x = (1ll * A * x + B) % C; if(l > r) swap(l, r); } int L = r - l + 1; p = GSAM::locate(pos[p][r - 1], L); int ans = BIT::query(dfn[p], dfn[p] + sz[p] - 1); ans += SEG::query(len[fa[p]] + 1, len[p], L, len[p], SEG::R[p]); cout << (lst = ans) << "\n"; } } cerr << 1e3 * clock() / CLOCKS_PER_SEC << " ms\n"; return 0; }
- 1
信息
- ID
- 8229
- 时间
- 2000ms
- 内存
- 1024MiB
- 难度
- 7
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者