CF1608G Alphabetic Tree
CF*3500 神仙题 & 毒瘤题,就是 CF547E 上树,难了不知多少。要不把 CF587F 也上个树。学习魏老师写的神秘倍增没看懂自上而下的链是怎么处理的,于是写了个树剖,写和调将近 4h。
给出一棵 \(n\) 个点的树,边 \(i\) 上有字母 \(c_i\)。定义 \(str(u,v)\) 为从点 \(u\) 走到点 \(v\) 途径边上的字母顺次拼接得到的字符串。形式化的,若点 \(u\) 到 点 \(v\) 路径上的边依次为 \(p_1,p_2,\dots,p_k\),则 \(str(u,v) = \overline{c_{p_1}c_{p_2}\dots c_{p_k}}\)。
你有 \(m\) 个字符串 \(s_1\sim s_m\) 和 \(q\) 个询问,每个询问形如 \(u\texttt{ }v\texttt{ }l\texttt{ }r\),你要回答 \(str(u,v)\) 在 \(s_l\sim s_r\) 中出现了几次。在一个串中重复出现算多次。
\(n,m,q,\sum\limits_{i=1}^m|s_i|\le 10^5\)。
考虑将答案表示成差分的形式并离线计算,即 \([1,r]\) 的答案减去 \([1,l)\) 的答案。扫描右端点,每次将端点的串加入贡献,然后回答所有端点为该点的询问。这是大体思路。
先套路将所有串用奇怪字符拼接起来(拼成的大串记为 \(S\)),做一遍后缀排序,求出 \(sa,rk,\text{height}\) 数组以及 \(\text{height}\) 数组的 ST 表。根据题意,下文默认 \(n,m,q,|S|\) 同阶。
记 \(hd_i\) 为 \(s_i\) 开头的后缀在 \(S\) 中出现的位置,\(suf_i\) 为 \(S\) 以 \(i\) 开头的后缀。则每个询问的答案为 \([1,hd_r+|s_r|]\) 的答案减去 \([1,hd_l)\) 的答案。
我们试图把 \(str(u,v)\) 表示成 \(s_l\sim s_r\) 的某个后缀的前缀的形式。而这些后缀的排名一定是连续的。
因为若不连续,则说明这中间存在一个后缀的该长度前缀的某一位与这些后缀不同,那么这两种后缀通过这个长度的前缀就能比较出字典序。若这个不同的后缀排在中间,则说明这两种后缀该长度前缀的字典序大小关系是既小于,又大于(因为排在那个不同的后缀排在中间说明该种后缀有一部分排在不同的后缀前面,另一部分排在不同的后缀后面,排在前面的字典序小,排在后面的字典序大),显然不对。
因此,我们想要求出这些后缀排名的区间。我们发现 \(str(u,v)\) 的字典序一定不大于这些后缀。因为这些后缀以 \(str(u,v)\) 为前缀,相当于在 \(str(u,v)\) 后面接上某个串,而 \(str(u,v)\) 本身可以看作 \(str(u,v)\) 接上一个空串(在后缀排序种认为空串的字典序最小),肯定是这些串中字典序最小的。
考虑二分出字典序大于等于 \(str(u,v)\) 的后缀的最小排名 \(rkl\)。设当前二分的排名为 \(mid\),我们要比较这两个串的大小。由于是树上询问,因此 \(\sum|str(u,v)|\) 可能会很大,不能将它们和 \(s_1\sim s_m\) 一起后缀排序。想象一下我们是怎么手动比较字典序大小的,肯定是先找一段 \(\mathbf{LCP}\),然后比较下一个不相等的位置。
考虑使用哈希。若按照常规方法直接使用树剖、线段树或树上倍增维护路径的哈希值再二分 \(|\text{LCP}|\),单次比较的时间复杂度会达到 \(\mathcal{O}(\log n \cdot \log |S|)\)。这么一来总的时间复杂度为 \(\mathcal{O}(q\log n\cdot \log^2|S|)\),无法接受(没试过,上一次卡过三只 log 还是在 P5327)。
我们发现二分 \(|\text{LCP}|\) 会产生很多无用的比较,所以考虑把这个 \(\log |S|\) 优化掉。我们知道,\(u\) 到 \(v\) 的路径会被分成 \(\mathcal{O}(\log n)\) 条重链。我们可以按照顺序比较一条条重链的哈希值,如果整条重链能匹配上就直接跳过,否则再二分找那个失配的位置。
具体地,先计算 \(S\) 的前缀哈希值 \(H\),再对树做一遍重链剖分,预处理每条重链自上而下的前缀哈希值 \(H_u\) 以及自下而上的后缀哈希值 \(H_d\)。还需要有三个求区间哈希值的函数。
每次询问 \(u,v\) 时,将路径上的重链、当前路径在该重链上经过了第几个位置到第几个位置、该重链是自上而下走还是自下而上走、按顺序存放在一个容器里。注意两个点跳到同一重链上的情况。这个过程还需要求出 \(\text{LCA}\)。
然后遍历容器,一段段与排名为 \(mid\) 的后缀的对应位置匹配。若能够匹配上,则将已匹配长度加上当前路径在该重链上的长度,并跳到下一条链继续匹配。否则二分寻找失配位置,将已匹配长度加上 \(|\text{LCP}|\),并比较失配位置的两个字符的大小。需要记一个 \(p\) 表示已经匹配好到该后缀的第 \(p\) 个位置。注意 \(\boldsymbol{p}\) 和已匹配长度的关系。你会感觉这个过程有点像值域分块。
这部分细节繁多,比如匹配的方向、需要匹配的长度、还未匹配的长度、以及一方匹配完之后如何比较大小等,可以看代码。不过我写的很抽象可能比较难看懂。为了方便,这个过程需要求出两个值,\(|\text{LCP}|\) 以及大小关系。
求出来后,若 \(|\text{LCP}|\) 不等于路径长度 \(len\),则不存在这样的后缀使得 \(str(u,v)\) 为其前缀。否则再二分找到最大的排名 \(rkr\) 使得 \(\text{LCP}(suf_{sa_{rkl}},suf_{sa_{rkr}}) \ge len\),这个用 \(\text{height}\) 数组的 ST 表求 \(\text{LCP}\) 就行了。
设当前扫描到的右端点为 \(rpos\),现在求出来了 \(rkl\) 和 \(rkr\),问题变成在 \(suf_{1}\sim suf_{rpos}\) 中,有多少后缀的排名在 \([rkl,rkr]\) 之间。这是一个简单的二维数点问题,使用树状数组维护,扫描时将 \(rk_{rpos}\) 插入到树状数组中,询问就是树状数组上区间 \([1,rkr]\) 的和减去区间 \([1,rkl)\) 的和。至此问题被解决。
时间复杂度为 \(\mathcal{O}(q\log n\cdot \log |S|)\),空间复杂度为 \(\mathcal{O}(|S|)\)。可以接受。
代码(\(8.06\,\text{KB}\)):
// LUOGU_RID: 125479078
#include <bits/stdc++.h>
#define P pair
#define fi first
#define se second
#define eb emplace_back
#define bg begin
#define vec vector
#define mst memset
#define ull unsigned long long
#define ll long long
using namespace std; const int N = 2e5 + 5; const ull B = 1145141;
int n, m, q, a[N], ord = 26, len, cnt[N], lg[N], sz[N], hd[N];
ull H[N], pw[N]; string s; vec<P<int, int>> g[N]; vec<ull> Hu[N], Hd[N];
struct range { int l, r, id; bool tp; }; vec<range> tmpx, tmpy, tmp; // 0 U 1 D
struct Query { int u, v, id, val; }; vec<Query> evt[N]; ll ans[N];
struct BinaryIndexedTree {
#define lb(x) ((x) & (-(x)))
ll sum[N]; void init() { mst(sum, 0, sizeof sum); }
void modify(int x, int v) { for (; x <= len; x += lb(x)) sum[x] += v; }
ll query(int x) {
ll ret = 0; for (; x; x -= lb(x)) ret += sum[x]; return ret;
}
} bit;
struct SuffixArray {
int rk[N], sa[N], ht[N], val[20][N]; P<P<int, int>, int> p[N], tmp[N];
void init() {
mst(rk, 0, sizeof rk); mst(val, 0, sizeof val);
mst(ht, 0, sizeof ht); mst(p, 0, sizeof p);
}
void Sort() {
for (int i = 1; i <= len; ++i) ++cnt[a[i]];
for (int i = 1; i <= ord; ++i) cnt[i] += cnt[i - 1];
for (int i = 1; i <= len; ++i) rk[i] = cnt[a[i] - 1] + 1;
for (int l = 1, id; l <= len; l <<= 1) {
for (int i = 1; i <= len; ++i)
p[i] = {{rk[i], i + l > len ? 0 : rk[i + l]}, i};
mst(cnt, 0, sizeof cnt);
for (int i = 1; i <= len; ++i) ++cnt[p[i].fi.se];
for (int i = 1; i <= len; ++i) cnt[i] += cnt[i - 1];
for (int i = len; i >= 1; --i) tmp[cnt[p[i].fi.se]--] = p[i];
for (int i = 1; i <= len; ++i) p[i] = tmp[i];
mst(cnt, 0, sizeof cnt);
for (int i = 1; i <= len; ++i) ++cnt[p[i].fi.fi];
for (int i = 1; i <= len; ++i) cnt[i] += cnt[i - 1];
for (int i = len; i >= 1; --i) tmp[cnt[p[i].fi.fi]--] = p[i];
for (int i = 1; i <= len; ++i) p[i] = tmp[i]; id = 0;
for (int i = 1; i <= len; ++i) {
if (i == 1 || p[i].fi != p[i - 1].fi) ++id; rk[p[i].se] = id;
}
if (id == len) break;
}
for (int i = 1; i <= len; ++i) sa[rk[i]] = i;
}
void height() {
for (int i = 1, k = 0; i <= len; ++i) {
lg[i] = __lg(i); if (rk[i] == 1) { k = 0; continue; }
if (k) --k; int j = sa[rk[i] - 1];
for (; i + k <= len && j + k <= len && a[i + k] == a[j + k]; ++k);
val[0][rk[i]] = ht[i] = k;
}
for (int i = 1; (1 << i) <= len; ++i)
for (int j = 1; j + (1 << i) - 1 <= len; ++j)
val[i][j] = min(val[i - 1][j], val[i - 1][j + (1 << (i - 1))]);
}
int LCP(int l, int r) {
if (l == r) return len - sa[l] + 1;
int k = lg[r - l]; return min(val[k][l + 1], val[k][r - (1 << k) + 1]);
}
} SA;
ull HashA(int l, int r) { return H[r] - H[l - 1] * pw[r - l + 1]; }
ull HashU(int id, int l, int r)
{ return Hu[id][l] - Hu[id][r + 1] * pw[r - l + 1]; }
ull HashD(int id, int l, int r)
{ return Hd[id][r] - Hd[id][l - 1] * pw[r - l + 1]; }
int dfn[N], idx, fa[N], siz[N], top[N], dep[N], hson[N], ed[N], val[N];
void dfs1(int u) {
siz[u] = 1;
for (auto v : g[u]) {
if (v.fi == fa[u]) continue; val[v.fi] = v.se;
dep[v.fi] = dep[fa[v.fi] = u] + 1; dfs1(v.fi); siz[u] += siz[v.fi];
}
}
void dfs2(int u) {
for (auto v : g[u]) {
if (v.fi == fa[u]) continue;
if (siz[v.fi] > siz[hson[u]]) hson[u] = v.fi;
}
for (auto v : g[u]) {
if (v.fi == fa[u]) continue;
if (v.fi == hson[u]) top[v.fi] = top[u]; else top[v.fi] = v.fi;
dfs2(v.fi);
}
}
void dfs3(int u) {
dfn[u] = ++idx; if (hson[u]) dfs3(hson[u]); else ed[top[u]] = u;
for (auto v : g[u])
{ if (v.fi == fa[u] || v.fi == hson[u]) continue; dfs3(v.fi); }
}
void CalD(int x) {
int u = x; ull b = 0; Hd[x].eb(0);
while (u) Hd[x].eb(b = b * B + val[u]), u = hson[u]; Hd[x].eb(0);
}
void CalU(int x) {
int u = x; ull b = 0; Hu[top[x]].eb(0);
while (top[u] == top[x]) Hu[top[x]].eb(b = b * B + val[u]), u = fa[u];
Hu[top[x]].eb(0); reverse(Hu[top[x]].bg(), Hu[top[x]].end());
}
int get(int x) { return dfn[x] - dfn[top[x]] + 1; }
int length(range x) { return x.r - x.l + 1; }
int lca(int x, int y) {
tmp.clear(), tmpx.clear(), tmpy.clear();
while (top[x] != top[y]) {
if (dep[top[x]] > dep[top[y]])
tmpx.eb(range{1, get(x), top[x], 0}), x = fa[top[x]];
else tmpy.eb(range{1, get(y), top[y], 1}), y = fa[top[y]];
}
reverse(tmpy.bg(), tmpy.end()); for (auto i : tmpx) tmp.eb(i);
if (x != y)
if (dep[x] < dep[y]) tmp.eb(range{get(x) + 1, get(y), top[x], 1});
else tmp.eb(range{get(y) + 1, get(x), top[x], 0});
for (auto i : tmpy) tmp.eb(i); return dep[x] < dep[y] ? x : y;
}
P<int, bool> check(int u, int v, int pos, int &dis) { // u -> v <= pos
int anc = lca(u, v), p = pos - 1;
dis = dep[u] + dep[v] - (dep[anc] << 1); int pl = min(dis, len - p);
for (auto i : tmp) {
int pip = min(pos + pl - p - 1, length(i));
if (i.tp)
if (HashD(i.id, i.l, i.l + pip - 1) != HashA(p + 1, p + pip)) {
int l = 1, r = pip, f = 0;
while (l <= r) {
int md = (l + r) >> 1;
if (HashD(i.id, i.l, i.l + md - 1) == HashA(p + 1, p + md))
f = md, l = md + 1;
else r = md - 1;
}
return {p + f - pos + 1,
HashD(i.id, i.l + f, i.l + f) < a[p + f + 1]};
} else p += pip;
else
if (HashU(i.id, i.l, i.l + pip - 1) != HashA(p + 1, p + pip)) {
int l = 1, r = pip, f = 0;
while (l <= r) {
int md = (l + r) >> 1;
if (HashU(i.id, i.r - md + 1, i.r) == HashA(p + 1, p + md))
f = md, l = md + 1;
else r = md - 1;
}
return {p + f - pos + 1,
HashU(i.id, i.r - f, i.r - f) < a[p + f + 1]};
} else p += pip;
if (p - pos + 1 == dis) return {dis, 1}; if (p - pos + 1 == pl) break;
}
return {pl, 0};
}
signed main() {
cin.tie(0), cout.tie(0), cin.sync_with_stdio(0); cin >> n >> m >> q;
for (int i = 1, u, v; i < n; ++i) {
char c; cin >> u >> v >> c;
g[u].eb(v, c - 'a' + 1); g[v].eb(u, c - 'a' + 1);
}
for (int i = 1; i <= m; ++i) {
cin >> s; sz[i] = s.size(); hd[i] = len + 1;
for (int j = 0; j < sz[i]; ++j) a[++len] = s[j] - 'a' + 1;
a[++len] = ++ord;
}
for (int i = pw[0] = 1; i <= len; ++i)
H[i] = H[i - 1] * B + a[i], pw[i] = pw[i - 1] * B;
SA.init(); SA.Sort(); SA.height(); bit.init();
for (int i = 1, l, r, u, v; i <= q; ++i) {
cin >> u >> v >> l >> r; evt[hd[l] - 1].eb(Query{u, v, i, -1});
evt[hd[r] + sz[r]].eb(Query{u, v, i, 1});
}
dfs1(1), dfs2(top[1] = 1), dfs3(1);
for (int i = 1; i <= n; ++i)
{ if (i == top[i]) CalD(i); if (i == ed[top[i]]) CalU(i); }
for (int i = 1; i <= len; ++i) {
bit.modify(SA.rk[i], 1);
for (auto j : evt[i]) {
int l = 1, r = len, f = 0, d, o;
while (l <= r) {
int mid = (l + r) >> 1;
if (check(j.u, j.v, SA.sa[mid], d).se) f = mid, r = mid - 1;
else l = mid + 1;
}
if (!f) continue; int k = check(j.u, j.v, SA.sa[f], d).fi;
if (k < d) continue; l = f, r = len;
while (l <= r) {
int mid = (l + r) >> 1;
if (SA.LCP(f, mid) >= d) o = mid, l = mid + 1; else r = mid - 1;
}
ans[j.id] += (bit.query(o) - bit.query(f - 1)) * j.val;
}
}
for (int i = 1; i <= q; ++i) cout << ans[i] << '\n'; return 0;
}
等等,你以为完了吗?万一出题人再恶毒一点,给你加个强制在线怎么办?虽然这是不可能的因为这场已经 downvote 170 了。
这个时候我们把离线树状数组换成主席树即可,将所有后缀 \(suf_i\) 插入主席树版本 \([1,i]\) 的 \(rk_i\) 位置。仍然求出 \(rkl\) 和 \(rkr\),然后把原来的离线差分变成 \([1,hd_r+|s_r|]\) 和 \([1,hd_l-1]\) 两个版本相减即可。
时间复杂度为 \(\mathcal{O}(q\log n\cdot \log |S|)\),空间复杂度为 \(\mathcal{O}(|S|\log |S|)\)。可以接受。
代码(\(8.24\,\text{KB}\)):
#include <bits/stdc++.h>
#define P pair
#define fi first
#define se second
#define eb emplace_back
#define bg begin
#define vec vector
#define mst memset
#define ull unsigned long long
#define ll long long
using namespace std; const int N = 2e5 + 5; const ull B = 1145141;
int n, m, q, a[N], ord = 26, len, cnt[N], lg[N], sz[N], hd[N];
ull H[N], pw[N]; string s; vec<P<int, int>> g[N]; vec<ull> Hu[N], Hd[N];
struct range { int l, r, id; bool tp; }; vec<range> tmpx, tmpy, tmp; // 0 U 1 D
struct ChairmanTree {
ll sum[N * 20]; int ls[N * 20], rs[N * 20], rt[N], cnt;
void init() {
memset(sum, cnt = 0, sizeof sum); memset(rt, 0, sizeof rt);
memset(ls, 0, sizeof ls); memset(rs, 0, sizeof rs);
}
void insert(int &x, int y, int l, int r, int pos) {
x = ++cnt; sum[x] = sum[y] + 1; if (l == r) return;
int mid = (l + r) >> 1;
if (pos <= mid) rs[x] = rs[y], insert(ls[x], ls[y], l, mid, pos);
else ls[x] = ls[y], insert(rs[x], rs[y], mid + 1, r, pos);
}
int query(int x, int y, int l, int r, int ql, int qr) {
if (ql <= l && r <= qr) return sum[x] - sum[y];
int mid = (l + r) >> 1, ret = 0;
if (ql <= mid) ret = query(ls[x], ls[y], l, mid, ql, qr);
if (qr > mid) ret += query(rs[x], rs[y], mid + 1, r, ql, qr);
return ret;
}
} cmt;
struct SuffixArray {
int rk[N], sa[N], ht[N], val[20][N]; P<P<int, int>, int> p[N], tmp[N];
void init() {
mst(rk, 0, sizeof rk); mst(val, 0, sizeof val);
mst(ht, 0, sizeof ht); mst(p, 0, sizeof p);
}
void Sort() {
for (int i = 1; i <= len; ++i) ++cnt[a[i]];
for (int i = 1; i <= ord; ++i) cnt[i] += cnt[i - 1];
for (int i = 1; i <= len; ++i) rk[i] = cnt[a[i] - 1] + 1;
for (int l = 1, id; l <= len; l <<= 1) {
for (int i = 1; i <= len; ++i)
p[i] = {{rk[i], i + l > len ? 0 : rk[i + l]}, i};
mst(cnt, 0, sizeof cnt);
for (int i = 1; i <= len; ++i) ++cnt[p[i].fi.se];
for (int i = 1; i <= len; ++i) cnt[i] += cnt[i - 1];
for (int i = len; i >= 1; --i) tmp[cnt[p[i].fi.se]--] = p[i];
for (int i = 1; i <= len; ++i) p[i] = tmp[i];
mst(cnt, 0, sizeof cnt);
for (int i = 1; i <= len; ++i) ++cnt[p[i].fi.fi];
for (int i = 1; i <= len; ++i) cnt[i] += cnt[i - 1];
for (int i = len; i >= 1; --i) tmp[cnt[p[i].fi.fi]--] = p[i];
for (int i = 1; i <= len; ++i) p[i] = tmp[i]; id = 0;
for (int i = 1; i <= len; ++i) {
if (i == 1 || p[i].fi != p[i - 1].fi) ++id; rk[p[i].se] = id;
}
if (id == len) break;
}
for (int i = 1; i <= len; ++i) sa[rk[i]] = i;
}
void height() {
for (int i = 1, k = 0; i <= len; ++i) {
lg[i] = __lg(i); if (rk[i] == 1) { k = 0; continue; }
if (k) --k; int j = sa[rk[i] - 1];
for (; i + k <= len && j + k <= len && a[i + k] == a[j + k]; ++k);
val[0][rk[i]] = ht[i] = k;
}
for (int i = 1; (1 << i) <= len; ++i)
for (int j = 1; j + (1 << i) - 1 <= len; ++j)
val[i][j] = min(val[i - 1][j], val[i - 1][j + (1 << (i - 1))]);
}
int LCP(int l, int r) {
if (l == r) return len - sa[l] + 1;
int k = lg[r - l]; return min(val[k][l + 1], val[k][r - (1 << k) + 1]);
}
} SA;
ull HashA(int l, int r) { return H[r] - H[l - 1] * pw[r - l + 1]; }
ull HashU(int id, int l, int r)
{ return Hu[id][l] - Hu[id][r + 1] * pw[r - l + 1]; }
ull HashD(int id, int l, int r)
{ return Hd[id][r] - Hd[id][l - 1] * pw[r - l + 1]; }
int dfn[N], idx, fa[N], siz[N], top[N], dep[N], hson[N], ed[N], val[N];
void dfs1(int u) {
siz[u] = 1;
for (auto v : g[u]) {
if (v.fi == fa[u]) continue; val[v.fi] = v.se;
dep[v.fi] = dep[fa[v.fi] = u] + 1; dfs1(v.fi); siz[u] += siz[v.fi];
}
}
void dfs2(int u) {
for (auto v : g[u]) {
if (v.fi == fa[u]) continue;
if (siz[v.fi] > siz[hson[u]]) hson[u] = v.fi;
}
for (auto v : g[u]) {
if (v.fi == fa[u]) continue;
if (v.fi == hson[u]) top[v.fi] = top[u]; else top[v.fi] = v.fi;
dfs2(v.fi);
}
}
void dfs3(int u) {
dfn[u] = ++idx; if (hson[u]) dfs3(hson[u]); else ed[top[u]] = u;
for (auto v : g[u])
{ if (v.fi == fa[u] || v.fi == hson[u]) continue; dfs3(v.fi); }
}
void CalD(int x) {
int u = x; ull b = 0; Hd[x].eb(0);
while (u) Hd[x].eb(b = b * B + val[u]), u = hson[u]; Hd[x].eb(0);
}
void CalU(int x) {
int u = x; ull b = 0; Hu[top[x]].eb(0);
while (top[u] == top[x]) Hu[top[x]].eb(b = b * B + val[u]), u = fa[u];
Hu[top[x]].eb(0); reverse(Hu[top[x]].bg(), Hu[top[x]].end());
}
int get(int x) { return dfn[x] - dfn[top[x]] + 1; }
int length(range x) { return x.r - x.l + 1; }
int lca(int x, int y) {
tmp.clear(), tmpx.clear(), tmpy.clear();
while (top[x] != top[y]) {
if (dep[top[x]] > dep[top[y]])
tmpx.eb(range{1, get(x), top[x], 0}), x = fa[top[x]];
else tmpy.eb(range{1, get(y), top[y], 1}), y = fa[top[y]];
}
reverse(tmpy.bg(), tmpy.end()); for (auto i : tmpx) tmp.eb(i);
if (x != y)
if (dep[x] < dep[y]) tmp.eb(range{get(x) + 1, get(y), top[x], 1});
else tmp.eb(range{get(y) + 1, get(x), top[x], 0});
for (auto i : tmpy) tmp.eb(i); return dep[x] < dep[y] ? x : y;
}
P<int, bool> check(int u, int v, int pos, int &dis) { // u -> v <= pos
int anc = lca(u, v), p = pos - 1;
dis = dep[u] + dep[v] - (dep[anc] << 1); int pl = min(dis, len - p);
for (auto i : tmp) {
int pip = min(pos + pl - p - 1, length(i));
if (i.tp)
if (HashD(i.id, i.l, i.l + pip - 1) != HashA(p + 1, p + pip)) {
int l = 1, r = pip, f = 0;
while (l <= r) {
int md = (l + r) >> 1;
if (HashD(i.id, i.l, i.l + md - 1) == HashA(p + 1, p + md))
f = md, l = md + 1;
else r = md - 1;
}
return {p + f - pos + 1,
HashD(i.id, i.l + f, i.l + f) < a[p + f + 1]};
} else p += pip;
else
if (HashU(i.id, i.l, i.l + pip - 1) != HashA(p + 1, p + pip)) {
int l = 1, r = pip, f = 0;
while (l <= r) {
int md = (l + r) >> 1;
if (HashU(i.id, i.r - md + 1, i.r) == HashA(p + 1, p + md))
f = md, l = md + 1;
else r = md - 1;
}
return {p + f - pos + 1,
HashU(i.id, i.r - f, i.r - f) < a[p + f + 1]};
} else p += pip;
if (p - pos + 1 == dis) return {dis, 1}; if (p - pos + 1 == pl) break;
}
return {pl, 0};
}
signed main() {
cin.tie(0), cout.tie(0), cin.sync_with_stdio(0); cin >> n >> m >> q;
for (int i = 1, u, v; i < n; ++i) {
char c; cin >> u >> v >> c;
g[u].eb(v, c - 'a' + 1); g[v].eb(u, c - 'a' + 1);
}
for (int i = 1; i <= m; ++i) {
cin >> s; sz[i] = s.size(); hd[i] = len + 1;
for (int j = 0; j < sz[i]; ++j) a[++len] = s[j] - 'a' + 1;
a[++len] = ++ord;
}
for (int i = pw[0] = 1; i <= len; ++i)
H[i] = H[i - 1] * B + a[i], pw[i] = pw[i - 1] * B;
dfs1(1), dfs2(top[1] = 1), dfs3(1);
for (int i = 1; i <= n; ++i)
{ if (i == top[i]) CalD(i); if (i == ed[top[i]]) CalU(i); }
SA.init(); SA.Sort(); SA.height(); cmt.init();
for (int i = 1; i <= len; ++i)
cmt.insert(cmt.rt[i], cmt.rt[i - 1], 1, len, SA.rk[i]);
for (int i = 1, l_, r_, u, v; i <= q; ++i) {
cin >> u >> v >> l_ >> r_;
int l = 1, r = len, f = 0, d, o;
while (l <= r) {
int mid = (l + r) >> 1;
if (check(u, v, SA.sa[mid], d).se) f = mid, r = mid - 1;
else l = mid + 1;
}
if (!f) { cout << "0\n"; continue; } int k = check(u, v, SA.sa[f], d).fi;
if (k < d) { cout << "0\n"; continue; } l = f, r = len;
while (l <= r) {
int mid = (l + r) >> 1;
if (SA.LCP(f, mid) >= d) o = mid, l = mid + 1; else r = mid - 1;
}
cout << cmt.query(cmt.rt[hd[r_] + sz[r_]], cmt.rt[hd[l_] - 1], 1, len, f, o) << '\n';
}
return 0;
}
两个代码的某些模板都封装了一下,感觉还是有点可读性的。有且仅有这么点可读性了。
完结撒花!

浙公网安备 33010602011771号