CF587F Duff is Mad [AC自动机,离线根号分治,树状数组]
题意:
here
求 \(s_{[l,r]}\) 在 \(s_k\) 的出现次数
很妙的一道题。
我们考虑到 \(AC\) 自动机的本质。
\(k\) 的子树里都是包含 \(s_k\) 的串。
在 \(k\) 的子树查询就相当于查询 \(s_k\) 出现了几次。
然后我们发现。
这个显然可以根号分治,大于 \(\sqrt {\sum len_i}\) 的和小于 \(\sqrt {\sum len_i}\) 的分别处理。
如果暴力做,是把 \(s_k\) 每个点设置成 \(1\),然后 \(l\) ~ \(r\) 都查询一遍子树和。
这样复杂度很高,但是可以拿来用。
我们对于 \(|s_k| \geq \sqrt {\sum len_i}\) 的,就把 \(l\) ~ \(r\) 记录一遍前缀和,相减就是。
我们考虑一下 \(s_k \leq \sqrt {\sum len_i}\) 的。
我们如何改变子树查询呢?
我们查询 \(l\) ~ \(r\) 都是查询子树对吧。
然后我们发现,你查询子树,其实无异于增加子树然后把 \(s_k\) 的贡献加进去。
简单来说呢,就是你可以计算出来每个点的覆盖次数,然后加进答案就好了。
然后这题没了。
#include <bits/stdc++.h>
int read() {
int x = 0;
char c = getchar();
while (c < 48) c = getchar();
while (c > 47) x = x * 10 + (c - 48), c = getchar();
return x;
}
using namespace std;
#define int long long
const int maxn = 1e5 + 51;
char s[maxn];
int n, q, S;
vector<pair<int, int>> L[maxn], R[maxn], le[maxn], ri[maxn];
int len[maxn], tot, ans[maxn];
struct BIT {
int c[maxn];
BIT() { memset(c, 0, sizeof(c)); }
int low(int x) { return x & -x; }
void add(int x, int y) {
for (; x < maxn; x += low(x)) c[x] += y;
}
int qry(int x) {
int ans = 0;
for (; x; x ^= low(x)) ans += c[x];
return ans;
}
} bit;
struct ACAM {
int ch[maxn][26], cnt, ed[maxn], fail[maxn], fa[maxn] ;
ACAM() { cnt = 1; }
vector<int> g[maxn];
void ins(const int& len, const int& id) {
int p = 1;
for (int i = 1; i <= len; i++) {
int c = s[i] - 'a';
if (!ch[p][c]) ch[p][c] = ++cnt, fa[cnt] = p;
p = ch[p][c];
}
ed[id] = p;
}
void build() {
queue<int> q;
for (int i = 0; i < 26; i++)
if (ch[1][i])
fail[ch[1][i]] = 1, q.push(ch[1][i]);
else
ch[1][i] = 1;
while (q.size()) {
int u = q.front();
q.pop();
for (int i = 0; i < 26; i++) {
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];
}
}
}
for (int i = 2; i <= cnt; i++) g[fail[i]].push_back(i);
}
int sz[maxn], dfn[maxn], idx = 0;
void dfs1(int u) {
for (int v : g[u]) dfs1(v), sz[u] += sz[v];
}
void dfs2(int u) {
sz[u] = 1, dfn[u] = ++idx;
for (int v : g[u]) dfs2(v), sz[u] += sz[v];
}
void solve() {
for (int i = 1; i <= n; i++) {
if (len[i] > S) {
int p = ed[i];
memset(sz, 0, sizeof(sz));
while (p ^ 1) {
sz[p] = 1, p = fa[p];
}
dfs1(1);
sort(L[i].begin(), L[i].end()), sort(R[i].begin(), R[i].end());
reverse(L[i].begin(), L[i].end()), reverse(R[i].begin(), R[i].end());
int qwq = 0;
for (int j = 1; j <= n; j++) {
while (L[i].size() && L[i].back().first == j) ans[L[i].back().second] -= qwq, L[i].pop_back();
qwq += sz[ed[j]];
while (R[i].size() && R[i].back().first == j) ans[R[i].back().second] += qwq, R[i].pop_back();
}
}
}
dfs2(1);
for (int i = 1; i <= n; i++) {
for (auto x : le[i]) {
int p = ed[x.first];
while (p ^ 1) {
ans[x.second] -= bit.qry(dfn[p]);
p = fa[p];
}
}
bit.add(dfn[ed[i]], 1);
bit.add(dfn[ed[i]] + sz[ed[i]], -1);
for (auto x : ri[i]) {
int p = ed[x.first];
while (p ^ 1) {
ans[x.second] += bit.qry(dfn[p]);
p = fa[p];
}
}
}
}
} acam;
signed main() {
n = read(), q = read();
for (int i = 1; i <= n; i++) {
scanf("%s", s + 1), len[i] = strlen(s + 1);
acam.ins(len[i], i);
tot += len[i];
}
acam.build(), S = sqrt(tot);
for (int i = 1; i <= q; i++) {
int l = read(), r = read(), k = read();
if (len[k] > S) {
L[k].push_back({ l, i });
R[k].push_back({ r, i });
} else {
le[l].push_back({ k, i });
ri[r].push_back({ k, i });
}
}
acam.solve();
for (int i = 1; i <= q; i++) printf("%lld\n", ans[i]);
return 0;
}