[SCOI2012]喵星球上的点名
SCOI 2012 喵题第二弹。
Description
给定 \(n\) 个喵的姓,名,均为一些长度为 \(k\) 的数字(数字大小属于 \(|S|\) ),以及 \(m\) 个询问,每次询问给定一个名字,所有姓或名包含这个名字的喵都要答到。
现在要你求每次询问答到的喵有几只,以及对于所有的 \(m\) 次询问,每只喵被点到了几次。
\(n \leq 5 \cdot 10 ^ 4,\ m \leq 10 ^ 5,\ \sum k \leq 10 ^ 5,\ |S| \leq 10 ^ 4\)
Analysis
能感觉到答案总数上限是在 \(n * m\) 的,正常的遇到一个就计数的方法肯定行不通。
于是各路神仙各显神通,有 [AC 自动机,SA,SAM] + [莫队,ST + 二分 + 线段树,树状数组] 的解法,那这里提供一种很自然的想法:
既然是多串匹配,那肯定会去想广义 SAM 呀!!1(不用加数据结构的)
Solution
假装你已经建好了 SAM 。
先看第一问。
根据题意,目标串只要是能出现在了猫的姓,名里面就要计数。一个串能贡献的地方是自己所在部分 SAM 上的,显然我们是没这个时间供我们每个点每个串跳 \(link\) 算的。
但是我们都知道跳 \(link\) 实际上在 SAM 上是一条一路到根的路径,所以照例建出后缀树,考虑可以树上差分。
对于每一个串,每个节点 \(x\) 加进来,每次新出现的地方就是从被上一个点 \(x - 1\) 跳 \(link\) 阻断的地方一直延伸到 \(x\) (其实就是 \(lca\) ),首加末减。最后遍历一遍后缀树就行了。
再看第二问。
同理向上和向下,后缀树中一个点的均包含祖先,同时被所有后代包含,所以仍然可以树上差分,然后只需要找到该点 \(dfn\) 序最大的那个( \(dfn + siz\) ),来一个首加末减也可以算完一个点出现的贡献。
计算的话和第一问一样的。
时间复杂度是 \(O(n\log n)\) 的, \(\log\) 是 \(lca\) 和 SAM 很少被提及的 \(\log |S|\) 访问的时间。
(看了题解之后才知道可以通过离线 tarjan 和基数排序分别解决这两个问题,但是 \(\log\) 足够了,摆烂)
那就做完咯,就是有点费码量。
Code
Code
/*
*/
#include
using namespace std;
typedef long long ll;
const int N = 2e5 + 10;
int n, m, l1[N], l2[N], S[N], cnt, f[N], co[N], g[N];
inline int read() {
char ch = getchar();
int s = 0, w = 1;
while (!isdigit(ch)) {if (ch == '-') w = -1; ch = getchar();}
while (isdigit(ch)) {s = (s << 3) + (s << 1) + (ch ^ 48); ch = getchar();}
return s * w;
}
struct edge {int nxt, to;};
struct mdzz {
int x, dfn;
bool operator < (const mdzz &it) const {
return dfn < it.dfn;
}
} d[N];
struct genSAM {
int cnt, las, len[N], link[N];
map ch[N];
int fst[N], tot, dfn[N], tim; edge e[N];
int siz[N], dep[N], son[N], top[N];
inline void init() {cnt = las = 1; ch[1].clear();}
inline void genSAM_stru(int c) {
if (ch[las][c]) {
int q = ch[las][c], p = las;
if (len[p] + 1 == len[q]) return las = q, void();
int clo = ++cnt;
link[clo] = link[q]; len[clo] = len[p] + 1;
link[q] = clo;
ch[clo] = ch[q];
while (p && ch[p][c] == q) ch[p][c] = clo, p = link[p];
return las = clo, void();
}
int cur = ++cnt, p = las;
ch[cur].clear();
las = cur;
len[cur] = len[p] + 1;
while (p && !ch[p][c]) ch[p][c] = cur, p = link[p];
if (!p) return link[cur] = 1, void();
int q = ch[p][c];
if (len[p] + 1 == len[q]) return link[cur] = q, void();
int clo = ++cnt;
link[clo] = link[q]; len[clo] = len[p] + 1;
link[q] = link[cur] = clo;
ch[clo] = ch[q];
while (p && ch[p][c] == q) ch[p][c] = clo, p = link[p];
}
inline void add(int u, int v) {
e[++tot] = (edge) {fst[u], v}; fst[u] = tot;
}
inline void dfs1(int u) {
dep[u] = dep[link[u]] + 1;
siz[u] = 1; dfn[u] = ++tim;
for (int i = fst[u], v; i; i = e[i].nxt) {
v = e[i].to;
if (v == link[u]) continue;
dfs1(v); siz[u] += siz[v];
if (siz[v] > siz[son[u]]) son[u] = v;
}
}
inline void dfs2(int u, int zs) {
top[u] = zs;
if (son[u]) dfs2(son[u], zs);
for (int i = fst[u], v; i; i = e[i].nxt) {
v = e[i].to;
if (v == link[u] || v == son[u]) continue;
dfs2(v, v);
}
}
inline int lca(int u, int v) {
while (top[u] ^ top[v]) dep[top[u]] > dep[top[v]] ? u = link[top[u]] : v = link[top[v]];
return dep[u] < dep[v] ? u : v;
}
inline void dfs(int u) {
for (int i = fst[u], v; i; i = e[i].nxt) {
v = e[i].to;
if (v == link[u]) continue;
dfs(v); f[u] += f[v];
}
}
} s;
int main() {
n = read(); m = read(); s.init();
for (int i = 1; i <= n; ++i) {
l1[i] = read(); s.las = 1;
for (int j = 1; j <= l1[i]; ++j) {
S[++cnt] = read();
s.genSAM_stru(S[cnt]);
}
l2[i] = read(); s.las = 1;
for (int j = 1; j <= l2[i]; ++j) {
S[++cnt] = read();
s.genSAM_stru(S[cnt]);
}
}
for (int i = 2; i <= s.cnt; ++i) s.add(s.link[i], i);
s.dfs1(1); s.dfs2(1, 1);
cnt = 0;
for (int i = 1, dc; i <= n; ++i) {
dc = 0;
for (int j = 1, v = 1; j <= l1[i]; ++j) {
v = s.ch[v][S[++cnt]];
d[++dc] = (mdzz) {v, s.dfn[v]};
}
for (int j = 1, v = 1; j <= l2[i]; ++j) {
v = s.ch[v][S[++cnt]];
d[++dc] = (mdzz) {v, s.dfn[v]};
}
sort(d + 1, d + 1 + dc);
++f[d[1].x];
for (int j = 2; j <= dc; ++j) {
++f[d[j].x]; --f[s.lca(d[j - 1].x, d[j].x)];
}
}
s.dfs(1);
while (m--) {
int l = read(), v = 1, fg = 0;
for (int i = 1, k; i <= l; ++i) {
k = read();
if (s.ch[v][k]) v = s.ch[v][k];
else fg = 1;
}
if (fg) printf("0\n");
else {
printf("%d\n", f[v]);
++co[s.dfn[v]]; --co[s.dfn[v] + s.siz[v]];
}
}
for (int i = 1; i <= s.cnt; ++i) co[i] += co[i - 1];
cnt = 0;
for (int i = 1, dc; i <= n; ++i) {
dc = 0;
for (int j = 1, v = 1; j <= l1[i]; ++j) {
v = s.ch[v][S[++cnt]];
d[++dc] = (mdzz) {v, s.dfn[v]};
}
for (int j = 1, v = 1; j <= l2[i]; ++j) {
v = s.ch[v][S[++cnt]];
d[++dc] = (mdzz) {v, s.dfn[v]};
}
sort(d + 1, d + 1 + dc);
g[i] += co[s.dfn[d[1].x]];
for (int j = 2; j <= dc; ++j) {
g[i] += co[s.dfn[d[j].x]]; g[i] -= co[s.dfn[s.lca(d[j - 1].x, d[j].x)]];
}
}
for (int i = 1; i <= n; ++i) printf("%d ", g[i]);
return 0;
}