P8571 题解
既然字符串的总长一定,不妨对于每个询问中的 \(s_k\) 的长度根号分治,假定分治阈值为 \(B\)。下面令 \(L\) 为所有串长度总和。
对于长度大于 \(B\) 的字符串,这样的不同字符串至多有 \(\frac{L}{B}\) 个,考虑对于每个字符串建立 AC 自动机,然后暴力匹配出其他字符串的出现次数,并 \(O(n)\) 建立一个线段树来回答询问,这一部分复杂度是 \(O(\frac{L^2}{B} + q \log n)\) 的。
对于长度小于等于 \(B\) 的字符串,它的前缀至多有 \(B\) 个,映射到 AC 自动机上 \(B\) 个点,我们要查询这些点在失配树上到祖先的路径上处于区间 \([l,r]\) 内的终止节点出现最多的终止节点出现次数。
不妨假定这个终止节点是点 \(x\),根据 AC 自动机的性质,其产生贡献的前缀节点在失配树上 \(x\) 的子树内,这提示我们,无论如何产生贡献的前缀节点都在某一个子树内。并且这个子树内的前缀节点也都产生了贡献。
所以考虑建出所有前缀节点的虚树,那么我们枚举虚树上的节点,问题转变为判断这个节点是否会产生贡献。
显然这是一个二维数点问题,不妨对 \(r\) 做一遍扫描线,问题转变为点修链查最大值,再将其转变为子树修改单点查最大值,考虑用一个 \(O(\sqrt L)\) 修改 \(O(1)\) 查询的分块维护,由于虚树大小是 \(O(B)\) 的,所以这部分复杂度是 \(O(L \sqrt L + L \times B + L \log L)\)。
显然当 \(B = \sqrt L\) 的时候取到最小值 \(O(L \sqrt L + q \log n + L \log L)\) 此时空间也是 \(O(L)\) 的。
注意时间空间常数问题。
要是你被卡常了可以尝试一个经典的 trick,将遍历树的过程用在树的 dfs 序上扫描完成。
#include <bits/stdc++.h>
#define query(x) (max(a[x],tag[bp[x]]))
using namespace std;
const int maxn = 1e5 + 5;
const int warma = 707;
string s[maxn];
int Len[maxn];
struct Query {
Query(int L, int R, int ID) {
l = L, r = R, id = ID;
}
int l, r, id;
};
int n, q;
vector<Query> Q[maxn];
int answer[maxn];
vector<int> Vtree;//虚树
int L[maxn * 5], R[maxn * 5], Node[maxn * 5];
int bp[maxn * 5];
int son[maxn * 5][26], fail[maxn * 5], rt, tot, dfncnt;
int sz[maxn * 5], Hson[maxn * 5], top[maxn * 5], dep[maxn * 5];
vector< pair<int, int>> w[maxn];
vector<int> edge[maxn * 5];
vector<int> fa[maxn];
vector<int> road[maxn * 5]; //虚树上的节点
int endpos[maxn * 5];
int found[maxn];
inline void insert(int pos) {
int len = s[pos].size(), now = rt;
for (register int i = 0; i < len; i = -~i) {
if (son[now][s[pos][i] - 'a'] == 0)
son[now][s[pos][i] - 'a'] = ++tot;
now = son[now][s[pos][i] - 'a'];
fa[pos].push_back(now);
}
if (endpos[now] == 0)
found[pos] = endpos[now] = pos;
else
found[pos] = endpos[now];
}
inline void build() {
queue<int> q;
for (register int i = 0; i < 26; i = -~i)
if (son[rt][i])
fail[son[rt][i]] = rt, q.push(son[rt][i]);
while (q.size() > 0) {
int u = q.front();
q.pop();
for (register int i = 0; i < 26; i = -~i) {
if (son[u][i]) {
fail[son[u][i]] = son[fail[u]][i];
q.push(son[u][i]);
} else
son[u][i] = son[fail[u]][i];
}
}
for (register int i = 1; i <= tot; i = -~i) {
edge[fail[i]].push_back(i);
}
}
inline void dfs(int u) {
L[u] = ++dfncnt, Node[dfncnt] = u, sz[u] = 1;
for (register int i = 0; i < edge[u].size(); i = -~i) {
dep[edge[u][i]] = dep[u] + 1;
dfs(edge[u][i]);
sz[u] += sz[edge[u][i]];
if (Hson[u] == -1 || sz[edge[u][i]] > sz[Hson[u]])
Hson[u] = edge[u][i];
}
R[u] = dfncnt;
}
inline void HLD(int u, int tp) {
top[u] = tp;
for (register int i = 0; i < edge[u].size(); i = -~i) {
if (edge[u][i] != Hson[u])
HLD(edge[u][i], edge[u][i]);
}
if (Hson[u] != -1)
HLD(Hson[u], tp);
}
inline int LCA(int u, int v) {
while (top[u] != top[v]) {
if (dep[top[u]] < dep[top[v]])
swap(u, v);
u = fail[top[u]];
}
if (dep[u] < dep[v])
swap(u, v);
return v;
}
int tag[maxn], a[maxn * 5];
inline void cover(int l, int r, int v) {
int bl = bp[l], br = bp[r];
if (bl == br) {
for (register int i = l; i <= r; i = -~i)
a[i] = v;
return ;
}
for (register int i = bl + 1; i < br; i = -~i)
tag[i] = v;
for (register int i = l; i <= bl * warma; i = -~i)
a[i] = v;
for (register int i = (br - 1) * warma + 1; i <= r; i = -~i)
a[i] = v;
}
struct ASK {
int l, k, id;
ASK(int L, int K, int ID) {
l = L, k = K, id = ID;
}
};
int tr[maxn << 2];
inline void build(int cur, int lt, int rt) {
if (lt == rt) {
tr[cur] = sz[fa[lt].back()];
return ;
}
int mid = (lt + rt) >> 1;
build(cur << 1, lt, mid);
build(cur << 1 | 1, mid + 1, rt);
tr[cur] = max(tr[cur << 1], tr[cur << 1 | 1]);
}
inline int query_mx(int cur, int l, int r, int lt, int rt) {
if (l <= lt && rt <= r)
return tr[cur];
if (r < lt || l > rt)
return 0;
int mid = (lt + rt) >> 1;
return max(query_mx(cur << 1, l, r, lt, mid), query_mx(cur << 1 | 1, l, r, mid + 1, rt));
}
inline bool cmp(int A, int B) {
return L[A] < L[B];
}
vector<ASK> ask[maxn];
inline void V_build(int u) {
for (register int i = 0; i < road[u].size(); i = -~i) {
int v = road[u][i];
V_build(v);
sz[u] += sz[v];
}
}
inline bool cmp1(pair<int, int> A, pair<int, int> B) {
return A.second > B.second;
}
inline string sread() {
string ans;
char ch = getchar();
while (ch < 'a' || ch > 'z')
ch = getchar();
while (ch >= 'a' && ch <= 'z')
ans += ch, ch = getchar();
return ans;
}
inline int read() {
bool flag = false;
int x = 0;
char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-')
flag = true;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = (x << 1) + (x << 3) + ch - '0';
ch = getchar();
}
return flag ? -x : x;
}
void write(int x) {
if (x < 0) {
putchar('-');
x = -x;
}
if (x > 9)
write(x / 10);
putchar(x % 10 ^ 48);
}
int flag[maxn];
int B;
set<int> S;
int root;
int main() {
memset(Hson, -1, sizeof(Hson));
n = read();
q = read();
for (register int i = 1; i <= n; i = -~i)
s[i] = sread(), Len[i] = s[i].size(), B += Len[i];
B = sqrt(B) * 1.4;
for (register int i = 1; i <= n; i = -~i)
insert(i);
for (register int i = 1; i <= q; i = -~i) {
int l, r, k;
l = read();
r = read();
k = read();
if (Len[k] <= B) {
flag[k] = 1;
ask[r].push_back(ASK(l, k, i));
} else {
Q[found[k]].push_back(Query(l, r, i));
}
}
build();
dfs(rt);
HLD(rt, rt);
for (register int i = 1; i <= dfncnt; i++)
bp[i] = (i - 1) / warma + 1;
for (register int i = 0; i <= tot; i++)
sz[i] = 0;
for (register int i = 1; i <= n; i = -~i) {
if (flag[i] == 0)
continue;
S.clear();
Vtree.clear();
root = -1;
for (register int j = 0; j < fa[i].size(); j = -~j)
Vtree.push_back(fa[i][j]), S.insert(fa[i][j]), sz[fa[i][j]]++;
sort(Vtree.begin(), Vtree.end(), cmp);
for (register int j = 0; j < Vtree.size() - 1; j = -~j) {
int u = LCA(Vtree[j], Vtree[j + 1]);
S.insert(u);
}
Vtree.clear();
for (set<int>::iterator i = S.begin(); i != S.end(); ++i) {
Vtree.push_back((*i));
if (root == -1 || dep[(*i)] < dep[root])
root = (*i);
}
sort(Vtree.begin(), Vtree.end(), cmp);
for (register int j = 0; j < Vtree.size() - 1; j = -~j) {
int u = LCA(Vtree[j], Vtree[j + 1]);
road[u].push_back(Vtree[j + 1]);
}
V_build(root);
for (register int j = 0; j < Vtree.size(); j = -~j)
w[i].push_back(make_pair(Vtree[j], sz[Vtree[j]]));
sort(w[i].begin(), w[i].end(), cmp1);
for (register int j = 0; j < Vtree.size(); j = -~j) {
sz[Vtree[j]] = 0;
road[Vtree[j]].clear();
}
}
for (register int i = 1; i <= n; i = -~i) {
cover(L[fa[i].back()], R[fa[i].back()], i);
for (register int j = 0; j < ask[i].size(); j = -~j) {
for (register int k = 0; k < w[ask[i][j].k].size(); k = -~k) {
if (query(L[w[ask[i][j].k][k].first]) >= ask[i][j].l) {
answer[ask[i][j].id] = max(answer[ask[i][j].id], w[ask[i][j].k][k].second);
break;
}
}
}
}
for (register int i = 1; i <= n; i = -~i) {
if (Q[i].size() > 0) {
for (register int j = 0; j <= tot; j = -~j)
sz[j] = 0;
for (register int j = 0; j < fa[i].size(); j = -~j) {
++sz[fa[i][j]];
}
for (int u = dfncnt; u >= 1; --u) {
if (Node[u] != rt)
sz[fail[Node[u]]] += sz[Node[u]];
}
build(1, 1, n);
for (register int j = 0; j < Q[i].size(); j = -~j) {
answer[Q[i][j].id] = query_mx(1, Q[i][j].l, Q[i][j].r, 1, n);
}
}
}
for (register int i = 1; i <= q; i = -~i)
write(answer[i]), putchar('\n');
return 0;
}
/*
6 1
a
aaa
dedicatus
misaka
mikoto
mi
1 2 2
*/