BZOJ 4231 回忆树
以下为自己口胡,未经网上搜索题解验证
Statement
一棵 个点的树,每条边有一个小写字母边权, 次询问,每次给定 ,问字符串 在 路径组成的字符串中出现了几次。.
Solution
Sol 1
把 路径拆成 两条路径,答案等于 中 的出现次数 中 的出现次数 经过 的出现次数。
“ 经过 的出现次数”由于 ,直接把 两边长度为 的串拼起来,跑 KMP / ACAM 即可。
问题变成分别求“ 中 的出现次数”与“ 中 的出现次数”。第一问可转化为“ 中 的出现次数”,第二问可转化为“ 中 的出现次数”,直接差分一下就是第一二问真正的答案。
“ 中 的出现次数”又可以转化为“ 中 的反串的出现次数”,于是问题变成了快速求 中 的出现次数。
把所有询问离线到每个 上,对于所有 建 ACAM,考虑 DFS 一遍求出所有答案。
当 向他的一个儿子 走,那么 对应的在 ACAM 上走一步 得到 。
可以这样解决:每次在 上单点加,对于询问 就查 ACAM 上 子树和就行了,树状数组可以维护,回溯时撤回修改。
Sol 2
树剖,每个重链维护 SAM,预处理 SAM 上每个点的 集合大小,对于重链直接按 在 SAM 上走即可,轻重链切换位置需要暴力匹配。.
不会其他做法了 QAQ
Code
#include <bits/stdc++.h> using namespace std; #define rep(i, j, k) for (int i = (j); i <= (k); ++i) #define reo(i, j, k) for (int i = (j); i >= (k); --i) typedef long long ll; const int N = 2e5 + 10, LN = 20, M = 6e5 + 10; struct Edge { int t, n; char c; } e[N << 1]; int n, m, tot, h[N]; void Add(int u, int v, char c) { e[++tot] = (Edge){v, h[u], c}, h[u] = tot; } int ln, dep[N], Fa[N][LN]; char Faw[N]; void DFS1(int u, int fa) { dep[u] = dep[fa] + 1, Fa[u][0] = fa; for (int i = h[u], v; i; i = e[i].n) { v = e[i].t; if (v != fa) { Faw[v] = e[i].c; DFS1(v, u); } } } void inittree() { DFS1(1, 0); ln = __lg(n); rep(j, 1, ln) rep(i, 1, n) Fa[i][j] = Fa[Fa[i][j - 1]][j - 1]; } int LCA(int u, int v) { if (dep[u] < dep[v]) swap(u, v); reo(i, ln, 0) if (dep[Fa[u][i]] >= dep[v]) u = Fa[u][i]; if (u == v) return u; reo(i, ln, 0) if (Fa[u][i] != Fa[v][i]) u = Fa[u][i], v = Fa[v][i]; return Fa[u][0]; } string s[N]; int ans[N]; struct Query { int id, xishu, sid; }; vector<Query> Queries[N]; int tim, dfn[M], sz[M]; vector<int> G[M]; void DFS3(int u) { dfn[u] = ++tim, sz[u] = 1; for (int v : G[u]) DFS3(v), sz[u] += sz[v]; } struct ACAM { int tot, ch[M][26], fail[M], cnt[M], ansid[N]; ACAM() { tot = 0; memset(ch, 0, sizeof(ch)); memset(fail, 0, sizeof(fail)); memset(cnt, 0, sizeof(cnt)); memset(ansid, 0, sizeof(ansid)); } void init() { reo(p, tot, 0) { rep(i, 0, 25) ch[p][i] = 0; fail[p] = 0, cnt[p] = 0; } tot = 0, ansid[0] = 0; } void ins(string& s, int id = 0) { int slen = s.length(), p = 0; rep(i, 0, slen - 1) { int now = s[i] - 'a'; if (!ch[p][now]) ch[p][now] = ++tot; p = ch[p][now]; } ++cnt[p], ansid[id] = p; } void Buildfail() { queue<int> q; rep(i, 0, 25) if (ch[0][i]) q.push(ch[0][i]); while (!q.empty()) { int u = q.front(); q.pop(); rep(i, 0, 25) if (ch[u][i]) fail[ch[u][i]] = ch[fail[u]][i], q.push(ch[u][i]); else ch[u][i] = ch[fail[u]][i]; } } int KMP(string& s) { int slen = s.length(), p = 0, ans = 0; rep(i, 0, slen - 1) p = ch[p][s[i] - 'a'], ans += cnt[p]; return ans; } void InitFailtree() { rep(i, 1, tot) G[fail[i]].push_back(i); DFS3(0); } } KMPAM, ACAM; struct BIT { int sum[M]; BIT() { memset(sum, 0, sizeof(sum)); } void upd(int x, int v) { for (; x <= ACAM.tot + 1; x += x & -x) sum[x] += v; } int qry(int x) { int res = 0; for (; x; x -= x & -x) res += sum[x]; return res; } int qry(int l, int r) { return qry(r) - qry(l - 1); } } bit; void DFS2(int u, int fa, int pos = 0) { bit.upd(dfn[pos], 1); for (auto qry : Queries[u]) { int now = ACAM.ansid[qry.sid]; ans[qry.id] += qry.xishu * bit.qry(dfn[now], dfn[now] + sz[now] - 1); } for (int i = h[u], v; i; i = e[i].n) { v = e[i].t; if (v != fa) { DFS2(v, u, ACAM.ch[pos][e[i].c - 'a']); } } bit.upd(dfn[pos], -1); } void SolveQueries() { ACAM.Buildfail(); ACAM.InitFailtree(); DFS2(1, 0); } int main() { ios::sync_with_stdio(false), cin.tie(nullptr); cin >> n >> m; rep(i, 1, n - 1) { int u, v; char c; cin >> u >> v >> c, Add(u, v, c), Add(v, u, c); } inittree(); rep(i, 1, m) { int u, v, lca; cin >> u >> v >> s[i]; s[i + m] = s[i]; reverse(s[i + m].begin(), s[i + m].end()); lca = LCA(u, v); int len = dep[u] + dep[v] - 2 * dep[lca], slen = s[i].length(); if (len < slen) continue; ACAM.ins(s[i], i), ACAM.ins(s[i + m], i + m); if (u == lca || v == lca) { if (u == lca) { Queries[v].push_back((Query){i, 1, i}); int p = v; reo(i, ln, 0) if (dep[Fa[p][i]] >= dep[u] + slen - 1) p = Fa[p][i]; Queries[p].push_back((Query){i, -1, i}); } else { Queries[u].push_back((Query){i, 1, i + m}); int p = u; reo(i, ln, 0) if (dep[Fa[p][i]] >= dep[v] + slen - 1) p = Fa[p][i]; Queries[p].push_back((Query){i, -1, i + m}); } } else { int p = -1, q = -1; if (dep[u] - dep[lca] >= slen) { Queries[u].push_back((Query){i, 1, i + m}); p = u; reo(i, ln, 0) if (dep[Fa[p][i]] >= dep[lca] + slen - 1) p = Fa[p][i]; Queries[p].push_back((Query){i, -1, i + m}); } if (dep[v] - dep[lca] >= slen) { Queries[v].push_back((Query){i, 1, i}); q = v; reo(i, ln, 0) if (dep[Fa[q][i]] >= dep[lca] + slen - 1) q = Fa[q][i]; Queries[q].push_back((Query){i, -1, i}); } if (p == -1) p = u; if (q == -1) q = v; KMPAM.init(); string t, tt; while (p != lca) t += Faw[p], p = Fa[p][0]; while (q != lca) tt += Faw[q], q = Fa[q][0]; reverse(tt.begin(), tt.end()); t += tt; KMPAM.ins(s[i]); KMPAM.Buildfail(); ans[i] += KMPAM.KMP(t); } } SolveQueries(); rep(i, 1, m) cout << ans[i] << '\n'; return 0; }
本文作者:Laijinyi
本文链接:https://www.cnblogs.com/laijinyi/p/18403596
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步