[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;
}

posted @ 2022-05-18 09:15  Illusory_dimes  阅读(46)  评论(0编辑  收藏  举报