bzoj 2754 [SCOI2012]喵星球上的点名 后缀数组+莫队

题面

题目传送门

解法

之前曾尝试用AC自动机暴力水过去,然而T了……

  • AC自动机显然是可以实现的,但是因为字符集太大,所以会导致超时。
  • 那么我们考虑后缀数组解决。首先我们可以将所有串整个拼成一个新的字符串。名和姓之间用一种分隔符隔开,不同的字符串之间用另一种分隔符隔开。
  • 然后构造出后缀数组。考虑如何求解第一问,显然可以在后缀数组中找到以当前字符串开头的后缀的位置,然后上下二分一下对应的区间 [ l , r ] [l,r] 满足排序后区间 [ l , r ] [l,r] 对应的后缀的前缀都为当前询问的字符串。然后就变成求解一个区间中有多少个不同的数的问题了,可以使用树状数组。此处使用莫队,因为这样方便求解第二问。
  • 然后我们考虑如何求解第二问。在莫队的时候,对于添加和删除分别处理。我们可以记录当前这个数上一次出现的位置在哪里。如果这个数在当前询问的区间中被删掉了,那么更新这个数的答案。如果这个数是第一次被添加进这个区间,那么更新这个数上一次出现的位置。
  • 时间复杂度: O ( n log n + n m ) O(n\log n+n\sqrt m)

代码

#include <bits/stdc++.h>
#define N 300010
using namespace std;
template <typename T> void read(T &x) {
	x = 0; int f = 1; char c = getchar();
	while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
	while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
struct Node {int l, r, id;} a[N];
int n, m, sum, c[N], s[N], y[N], sa[N], st[N], ans[N], las[N], len[N], num[N], pos[N], rnk[N], f[N][21];
bool cmp(Node a, Node b) {return (a.l >> 9) == (b.l >> 9) ? a.r < b.r : a.l < b.l;}
void del(int x, int y) {s[c[x]]--; if (!s[c[x]]) sum--, num[c[x]] += y - las[c[x]];}
void ins(int x, int y) {if (!s[c[x]]) sum++, las[c[x]] = y; s[c[x]]++;}
int lcp(int i, int j) {
	int l = i + 1, r = j, t = log2(r - l + 1);
	return min(f[l][t], f[r - (1 << t) + 1][t]);
}
void Sort() {
	for (int i = 1; i <= m; i++) s[i] = 0;
	for (int i = 1; i <= n; i++) s[rnk[i]]++;
	for (int i = 1; i <= m; i++) s[i] += s[i - 1];
	for (int i = n; i; i--) sa[s[rnk[y[i]]]--] = y[i];
}
void build() {
	for (int i = 1; i <= n; i++) rnk[i] = st[i], y[i] = i;
	m = 1e4 + 10, Sort(); int len = 0;
	for (int k = 1; k <= n; k <<= 1, m = len, len = 0) {
		for (int i = n - k + 1; i <= n; i++) y[++len] = i;
		for (int i = 1; i <= n; i++) if (sa[i] > k) y[++len] = sa[i] - k;
		Sort(), swap(rnk, y), rnk[sa[1]] = len = 1;
		for (int i = 2; i <= n; i++)
			rnk[sa[i]] = y[sa[i - 1]] == y[sa[i]] && y[sa[i - 1] + k] == y[sa[i] + k] ? len : ++len;
		if (len >= n) break;
	}
	for (int i = 1, k = 0; i <= n; i++) {
		if (rnk[i] == 1) continue;
		if (k) k--; int j = sa[rnk[i] - 1];
		while (st[i + k] == st[j + k]) k++;
		f[rnk[i]][0] = k;
	}
	for (int j = 1; (1 << j) <= n; j++)
		for (int i = 1; i + (1 << j) - 1 <= n; i++)
			f[i][j] = min(f[i][j - 1], f[i + (1 << j - 1)][j - 1]);
}
int main() {
	int tot, q; read(tot), read(q);
	for (int i = 1; i <= tot; i++) {
		int l; read(l);
		while (l--) read(st[++n]);
		st[++n] = 1e4 + 1, read(l);
		while (l--) read(st[++n]);
		st[++n] = 1e4 + 2;
	}
	int tmp = n, cnt = 1;
	for (int i = 1; i <= q; i++) {
		int l; read(l);
		pos[i] = n + 1, len[i] = l;
		while (l--) read(st[++n]);
		if (i != q) st[++n] = 1e4 + 2;
	}
	build();
	for (int i = 1; i <= tmp; i++) if (st[i] == 1e4 + 2) cnt++; else c[rnk[i]] = cnt;
	for (int i = 1; i <= q; i++) {
		int x = rnk[pos[i]], L = len[i];
		int l = 1, r = x - 1, tl = x;
		while (l <= r) {
			int mid = (l + r) >> 1;
			if (lcp(mid, x) >= L) tl = mid, r = mid - 1;
				else l = mid + 1;
		}
		l = x + 1, r = n; int tr = x;
		while (l <= r) {
			int mid = (l + r) >> 1;
			if (lcp(x, mid) >= L) tr = mid, l = mid + 1;
				else r = mid - 1;
		}
		a[i] = (Node) {tl, tr, i};
	}
	sort(a + 1, a + q + 1, cmp);
	memset(s, 0, sizeof(s)), sum = 0;
	for (int i = 1, l = 1, r = 0; i <= q; i++) {
		while (r < a[i].r) ins(++r, i);
		while (r > a[i].r) del(r--, i);
		while (l < a[i].l) del(l++, i);
		while (l > a[i].l) ins(--l, i);
		ans[a[i].id] = sum - (s[0] > 0);
	}
	for (int i = 1; i <= q; i++) cout << ans[i] << "\n";
	for (int i = 1; i <= tot; i++) if (s[i]) num[i] += q - las[i] + 1;
	for (int i = 1; i <= tot; i++) cout << num[i] << ' '; cout << "\n";
	return 0;
}

猜你喜欢

转载自blog.csdn.net/emmmmmmmmm/article/details/84197821