P4947 PION后缀自动机

给出一棵 \(n\) 个点的树,每个点上有若干字符串,有 \(m\) 次操作,分为三种:
\(\texttt{query /p }u\texttt{ }v\),查询 \(u,v\) 简单路径上的边数。
\(\texttt{query /e }u \texttt{ }v\texttt{ *.}s\),查询 \(u,v\) 简单路径上字符串 \(s\) 出现了几次。
\(\texttt{del /e }u \texttt{ }v\texttt{ *.}s\),查询 \(u,v\) 简单路径上字符串 \(s\) 出现了几次,并删除这些 \(s\)。
\(n,m\le 10^5\),字符串总数 \(k\) 不超过 \(5\times 10^5\),字符串长度不超过 \(8\)。
\(\texttt{query /p}\) 操作比较简单,答案为 \(dep_u+dep_v-2\times dep_{\text{LCA}(u,v)}\)。考虑解决剩下两个操作。
字符串的种数很少,考虑使用 P5838 的套路,有两种方法:
-
【方法一】
对每种字符串维护一棵动态开点线段树,线段树的节点维护对应 \(dfn\) 序区间内该字符串的出现次数。
-
对于 \(\texttt{query /e}\) 操作,跳链时在对应的线段树上查询当前重链的贡献。
-
对于 \(\texttt{del /e}\) 操作,则先做一遍 \(\texttt{query /e}\) 操作,然后再进行删除。删除的时候,在跳链时将当前重链对应的 \(dfn\) 区间推平成 \(0\)。
时间复杂度为 \(\mathcal{O}(m\log^2 n)\),空间复杂度为 \(\mathcal{O}(k\log n)\)。
-
-
【方法二】
对每种字符串维护一棵平衡树(这里使用
__gnu_pbds::tree),有序存放有该字符的点的 \(dfn\) 序,注意可能会重复,所以元素类型为pair,second的作用是区分同一个点的两个相同字符串。-
对于 \(\texttt{query /e}\) 操作,设当前在点 \(u\),跳链时使用
order_of_key找到在 \([1,dfn_{top_u})\) 和 \([1,dfn_u]\) 中的值个数,相减即为当前重链上该字符串的出现次数。 -
对于 \(\texttt{del /e}\) 操作,同样先做一遍 \(\texttt{query /e}\) 操作,然后再进行删除。删除的时候,使用
lower_bound找到第一个大于等于 \(dfn_{top_u}\) 的迭代器 \(l\) 和第一个大于 \(dfn_u\) 的迭代器 \(r\),暴力删除 \([l,r)\) 之间的所有迭代器。
看上去很暴力,但是一开始插入了 \(k\) 个元素,一个元素只能被删除一次,因此时间复杂度为 \(\mathcal{O}(m\log^2 n)\),空间复杂度为 \(\mathcal{O}(k+n)\)。
-
给的是【方法二】的代码。笔者因为看到 P7735 的一种新写法,即求出 \(\texttt{LCA}(u,v)\) 后分别算 \(u\rightsquigarrow \text{LCA}(u,v)\) 和 \(v\rightsquigarrow \text{LCA}(u,v)\) 的信息,以为会更好写,结果那种写法会算重 \(\text{LCA}(u,v)\) 的信息。在 P7735 中单点是没有贡献的,但是这题有,于是调了很久才发现要单独减去 \(\text{LCA}(u,v)\) 的贡献。因此部分代码不建议读者参考,还是建议读者去参考更常见的跳链查询。
#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/hash_policy.hpp>
#define G __gnu_pbds
#define TOSNU tree_order_statistics_node_update
#define rnk order_of_key
#define lb lower_bound
#define pii pair<int, int>
#define P make_pair
#define fi first
#define se second
#define vec vector
#define umap unordered_map
using namespace std; const int N = 1e5 + 5, inf = 1e9; typedef string str;
vec<int> g[N]; vector<pii> bin; vec<str> a[N]; str op1, op2, op3;
int n, m, dep[N], top[N], fa[N], hson[N], idx, dfn[N], siz[N], file;
umap<str, G::tree<pii, G::null_type, less<pii>, G::rb_tree_tag, G::TOSNU>> rbt;
void dfs1(int u) {
siz[u] = 1;
for (int v : g[u])
if (v != fa[u]) dep[v] = dep[fa[v] = u] + 1, dfs1(v), siz[u] += siz[v];
}
void dfs2(int u) {
for (int v : g[u]) {
if (v == fa[u]) continue;
if ((siz[v] << 1) > siz[u]) top[hson[u] = v] = top[u];
else top[v] = v; dfs2(v);
}
}
void dfs3(int u) {
dfn[u] = ++idx; for (str s : a[u]) rbt[s].insert(P(idx, ++file));
if (hson[u]) dfs3(hson[u]);
for (int v : g[u]) if (v != fa[u] && v != hson[u]) dfs3(v);
}
int LCA(int u, int v) {
while (top[u] != top[v]) {
if (dep[top[u]] < dep[top[v]]) swap(u, v);
u = fa[top[u]];
}
return dep[u] < dep[v] ? u : v;
}
int chain(int u, int v, str suf) {
int ret = 0;
while (top[u] != top[v]) {
ret += rbt[suf].rnk(P(dfn[u], inf)) - rbt[suf].rnk(P(dfn[top[u]], 0));
u = fa[top[u]];
}
return ret + rbt[suf].rnk(P(dfn[u], inf)) - rbt[suf].rnk(P(dfn[v], 0));
}
void erase(int u, int v, str suf) {
while (top[u] != top[v]) {
auto l = rbt[suf].lb(P(dfn[top[u]], 0)), r = rbt[suf].lb(P(dfn[u], inf));
bin.clear(); for (auto it = l; it != r; ++it) bin.emplace_back(*it);
for (pii i : bin) rbt[suf].erase(i); u = fa[top[u]];
}
auto l = rbt[suf].lb(P(dfn[v],0)), r = rbt[suf].lb(P(dfn[u], inf));
bin.clear(); for (auto it = l; it != r; ++it) bin.emplace_back(*it);
for (pii i : bin) rbt[suf].erase(i);
}
int query(int u, int v, int lca, str suf) {
if (lca == u) return chain(v, u, suf);
if (lca == v) return chain(u, v, suf);
return chain(u, lca, suf) + chain(v, lca, suf) - chain(lca, lca, suf);
}
void del(int u, int v, int lca, str suf) {
if (lca == u) return erase(v, u, suf), void();
if (lca == v) return erase(u, v, suf), void();
erase(u, lca, suf), erase(v, lca, suf);
}
signed main() {
cin.tie(0), cout.tie(0), ios::sync_with_stdio(0); cin >> n >> m;
for (int i = 1, u, v; i < n; ++i)
cin >> u >> v, g[u].emplace_back(v), g[v].emplace_back(u);
for (int i = 1, x; i <= n; ++i) {
cin >> x;
for (str tmp; x--;) cin >> tmp, a[i].emplace_back(tmp);
}
dfs1(1), dfs2(top[1] = 1), dfs3(1);
for (int i = 1, u, v, lca; i <= m; ++i) {
cin >> op1 >> op2 >> u >> v; lca = LCA(u, v);
if (op2 == "/p")
cout << dep[u] + dep[v] - (dep[lca] << 1) << '\n';
else {
cin >> op3; op3 = op3.substr(2);
cout << query(u, v, lca, op3) << '\n';
if (op1 == "del") del(u, v, lca, op3);
}
}
return 0;
}

浙公网安备 33010602011771号