[NOI2012] 阿狸的打字机
填坑。
题目给了一个trie树,询问又是类似字符串匹配,容易想到建立AC自动机。
模拟字符串匹配的过程,发现 \(x\) 在 \(y\) 中出现次数相当于在原来的trie中 \(y\) 有多少个祖先一直跳fail能跳到 \(x\)。 我们新建一颗fail树,从 \(fail[i]\) 向 \(i\) 连边,容易发现答案就是 \(y\) 在trie有多少个祖先在 \(x\) 这棵子树内。
有个显然的的在线做法是对trie进行树链剖分,两个序列两段区间求交可以转化为二维数点主席树解决。但是不用这么麻烦。我们考虑离线询问,dfs一遍trie树,在进入一个点 \(i\) 的时候 \(i\) 位置上的值+1,出去的时候-1,此时 \(y=i\) 的询问就转化成了 fail树上 \(x\) 的子树和。dfs序+树状数组维护即可。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;
const int N = 1e5 + 5;
struct node {
int x, q;
inline node(int X, int Q) : x(X), q(Q) { }
};
vector<node> qu[N];
int ch[N][26], ch2[N][26], cnt = 1, fa[N], p[N], top = 0, ans[N], tree[N], dfn[N], tim = 0, siz[N], fail[N];
inline void add(int x, int k) {
for (int i = x; i <= cnt; i += i & -i) tree[i] += k;
}
inline int sum(int x) {
int ret = 0;
for (int i = x; i; i -= i & -i) ret += tree[i];
return ret;
}
inline void build() {
queue<int> q;
for (int i = 0; i < 26; ++i)
if (ch[1][i]) q.push(ch[1][i]), fail[ch[1][i]] = 1;
while (!q.empty()) {
int x = q.front(); q.pop();
for (int i = 0; i < 26; ++i) {
if (!ch[x][i]) ch[x][i] = ch[fail[x]][i];
else {
fail[ch[x][i]] = ch[fail[x]][i];
q.push(ch[x][i]);
}
}
}
}
struct edge {
int head, to, nxt;
} ed[N << 1];
int en = 0;
inline void addedge(int from, int to) {
ed[++en].to = to; ed[en].nxt = ed[from].head; ed[from].head = en;
}
inline void dfs1(int now, int f) {
dfn[now] = ++tim; siz[now] = 1;
for (int i = ed[now].head; i; i = ed[i].nxt) {
int v = ed[i].to;
if (v == f) continue;
dfs1(v, now); siz[now] += siz[v];
}
}
inline void dfs2(int now) {
add(dfn[now], 1);
for (vector<node> :: iterator it = qu[now].begin(); it != qu[now].end(); ++it)
ans[(*it).q] = sum(dfn[(*it).x] + siz[(*it).x] - 1) - sum(dfn[(*it).x] - 1);
for (int i = 0; i < 26; ++i)
if (ch2[now][i]) dfs2(ch2[now][i]);
add(dfn[now], -1);
}
inline int read() {
register int s = 0; register char ch = getchar();
while (!isdigit(ch)) ch = getchar();
while (isdigit(ch)) s = (s << 1) + (s << 3) + (ch & 15), ch = getchar();
return s;
}
int main() {
int now = 1; char c = getchar(); memset(ch, 0, sizeof ch);
while (!isalpha(c)) c = getchar();
while (isalpha(c)) {
if (c != 'B' && c != 'P') {
if (!ch[now][c - 'a']) ch[now][c - 'a'] = ch2[now][c - 'a'] = ++cnt, fa[cnt] = now;
now = ch[now][c - 'a'];
} else if (c == 'P') p[++top] = now;
else if (c == 'B') now = fa[now];
c = getchar();
} build(); int m, x, y; m = read();
for (int i = 1; i <= m; ++i) {
x = read(); y = read();
qu[p[y]].push_back(node(p[x], i));
} for (int i = 2; i <= cnt; ++i) addedge(!fail[i] ? 1 : fail[i], i);
dfs1(1, 0); dfs2(1);
for (int i = 1; i <= m; ++i) printf("%d\n", ans[i]);
return 0;
}