题目大意
给出 n n 个人的姓和名,同时给出 段字符串。
对于每一段字符串询问其出现在多少个人的姓或者名里(一个人出现多次只计算一次)
最后询问每一个人在给出所有字符串之后包含了多少段字符串(同一段字符串包含多次只计算一次)
数据范围
1≤n≤2×104,1≤m≤5×104,1≤∑∣Si∣≤2×105 1 ≤ n ≤ 2 × 10 4 , 1 ≤ m ≤ 5 × 10 4 , 1 ≤ ∑ ∣ S i ∣≤ 2 × 10 5
思路
现在考虑第一个询问,观察一下,COCI2015 Divljak,我们考虑在
fail
f
a
i
l
上查询,那么一段字符串
S
S
出现在多少个母串中的意思就是 的末尾节点的子树中有多少个母串的节点(多个来自同一母串的节点只计算一次),也就是子树查询
我们把所有的串全部插到 trie t r i e 树中建出 fail f a i l 树,考虑一个母串会对哪些查询串作出贡献,在 trie t r i e 树上的每一个节点往根的路径的并集,那么我们要维护的就是路径的并集权值加 1 考虑 dfs d f s 序维护信息,把所有插入时遍历的 trie t r i e 树节点拿出来,按照 dfs d f s 序排序之后,将所有拿出来的点的权值加 1,相邻两个点的 lca l c a 处的权值减去重复计算的 1
维护好信息之后,对于每一个查询串在树状数组中子树查询一下即可。
现在考虑第二个询问,我们要查的就是姓名串在 fail f a i l 树上所有点往根路径的并集,其中如果是 查询串的结尾,那么该点的值为 1
类似上面的方法,先预处理出 fail f a i l 树上每一个节点到根的路径权值和,把所有姓名串插入时遍历的节点拿出来,按照 dfs d f s 序排序之后,加上所有点的权值和,再减去相邻两点之间 lca l c a 的权值和即可
PE有点无语
#include <cstdio>
#include <vector>
#include <map>
#include <cstring>
#include <algorithm>
using std :: vector;
using std :: map;
template <typename Tp>Tp Max(const Tp &a, const Tp &b) {return a > b ? a : b;}
template <typename Tp>Tp Min(const Tp &a, const Tp &b) {return a < b ? a : b;}
template <typename Tp>void Read(Tp &x) {
Tp in = 0, f = 1; char ch = getchar();
while(ch<'0' || ch>'9') {if(ch=='-') f = -1; ch = getchar();}
while(ch>='0' && ch<='9') {in = in * 10 + ch - '0'; ch = getchar();}
x = in * f;
}
const int SN = 200000 + 10;
map<int,int> ch[SN];
vector<int> S[SN], A1[SN], A2[SN], A3[SN];
int fail[SN], sum[SN], que[SN], pre[SN], net[SN], P[SN][2], head[SN], is_end[SN], val[SN];
int n, m, len, CLOCK, TIME, cnt, num;
int A[SN], s[SN];
struct Edge {
int v, next;
}E[SN];
void Add_E(int u, int v) {
E[++num].v = v, E[num].next = head[u], head[u] = num;
}
namespace Tree {
int d[SN], size[SN], son[SN], fa[SN], top[SN];
void Dfs(int u) {
size[u] = 1;
for(int i = head[u]; i; i = E[i].next) {
int to = E[i].v;
if(to == fa[u]) continue ;
fa[to] = u, d[to] = d[u] + 1;
Dfs(to);
size[u] += size[to];
if(size[to] >= size[son[u]] || !son[u]) son[u] = to;
}
}
void Dfs(int u, int t) {
top[u] = t;
if(son[u]) Dfs(son[u], t);
else return ;
for(int i = head[u]; i; i = E[i].next) {
int to = E[i].v;
if(to != son[u] && to != fa[u]) Dfs(to, to);
}
}
int LCA(int x, int y) {
while(top[x] != top[y]) {
if(d[top[x]] > d[top[y]]) std :: swap(x, y);
y = fa[top[y]];
}
return d[x] < d[y] ? x : y;
}
}
void Insert(int len, int num, int flag) {
int now = 0, to;
for(int i = 1; i <= len; i++) {
to = s[i];
if(!ch[now][to]) S[now].push_back(to), ch[now][to] = ++TIME;
now = ch[now][to];
}
is_end[num] = now;
if(flag) val[now]++;
}
void Getfail() {
int front = 1, tail = 0;
vector<int> :: iterator it;
for(it = S[0].begin(); it != S[0].end(); it++)
Add_E(0, ch[0][(*it)]), que[++tail] = ch[0][(*it)];
while(front <= tail) {
int now = que[front++], u;
for(it = S[now].begin(); it != S[now].end(); it++) {
u = ch[now][(*it)];
que[++tail] = u;
int v = fail[now];
while(v && !ch[v][(*it)]) v = fail[v];
fail[u] = ch[v][(*it)], Add_E(fail[u], u);
}
}
}
void Dfs(int u, int fa) {
pre[u] = ++CLOCK;
for(int i = head[u]; i; i = E[i].next) {
int to = E[i].v;
if(to == fa) continue ;
Dfs(to, u);
}
net[u] = CLOCK;
}
void Add(int x, int C) {
if(x == 0) return ;
while(x <= CLOCK) sum[x] += C, x += x & -x;
}
int Ask(int x) {
int res = 0;
while(x) res += sum[x], x -= x & -x;
return res;
}
bool cmp(int a, int b) {
return pre[a] < pre[b];
}
void Solve(int u, int fa) {
for(int i = head[u]; i; i = E[i].next) {
int to = E[i].v;
if(to == fa) continue ;
sum[to] = sum[u] + val[to];
Solve(to, u);
}
}
int main() {
int x, y;
Read(n), Read(m);
for(int i = 1; i <= n; i++) {
Read(x);
for(int j = 1; j <= x; j++) Read(s[j]), A1[i].push_back(s[j]);
Insert(x, ++cnt, 0), P[i][0] = cnt;
Read(x);
for(int j = 1; j <= x; j++) Read(s[j]), A2[i].push_back(s[j]);
Insert(x, ++cnt, 0), P[i][1] = cnt;
}
for(int i = 1; i <= m; i++) {
Read(x);
for(int j = 1; j <= x; j++) Read(s[j]), A3[i].push_back(s[j]);
Insert(x, ++cnt, 1), P[n + i][0] = cnt;
}
Getfail();
Dfs(0, -1);
Tree :: Dfs(0);
Tree :: Dfs(0, 0);
vector<int> :: iterator it;
for(int i = 1; i <= n; i++) {
int now = 0;
A[0] = 0;
for(it = A1[i].begin(); it != A1[i].end(); it++)
now = ch[now][(*it)], A[++A[0]] = now;
now = 0;
for(it = A2[i].begin(); it != A2[i].end(); it++)
now = ch[now][(*it)], A[++A[0]] = now;
std :: sort(A + 1, A + A[0] + 1, cmp);
A[0] = std :: unique(A + 1, A + A[0] + 1) - A - 1;
for(int i = 1; i <= A[0]; i++)
Add(pre[A[i]], 1);
for(int i = 2; i <= A[0]; i++)
Add(pre[Tree :: LCA(A[i], A[i - 1])], -1);
}
for(int i = 1; i <= m; i++)
printf("%d\n", Ask(net[is_end[P[n + i][0]]]) - Ask(pre[is_end[P[n + i][0]]] - 1));
memset(sum, 0, sizeof sum);
Solve(0, -1);
for(int i = 1; i <= n; i++) {
int now = 0, ans = 0;
A[0] = 0;
for(it = A1[i].begin(); it != A1[i].end(); it++)
now = ch[now][(*it)], A[++A[0]] = now;
now = 0;
for(it = A2[i].begin(); it != A2[i].end(); it++)
now = ch[now][(*it)], A[++A[0]] = now;
std :: sort(A + 1, A + A[0] + 1, cmp);
A[0] = std :: unique(A + 1, A + A[0] + 1) - A - 1;
for(int j = 1; j <= A[0]; j++)
ans += sum[A[j]];
for(int j = 2; j <= A[0]; j++)
ans -= sum[Tree :: LCA(A[j], A[j - 1])];
if(i == 1) printf("%d", ans);
else printf(" %d", ans);
}
return 0;
}