BZOJ2754: [SCOI2012]喵星球上的点名

题目地址

BZOJ2754: [SCOI2012]喵星球上的点名

题解

做法:后缀数组+ST表+树状数组。
先把所有串拼在一起,处理出SA数组,然后分别考虑两个问题。

对于第一个问题,首先对于每个询问,能够对答案有贡献的串一定在连续的一个区间且包含询问串(在经过后缀排序之后)。因为\(\text {LCP(x,y)} = \min_{k=x+1}^y\{ \text{height[k]}\}\),所以可以先预处理一下ST表,然后二分找出对每个询问有贡献的区间,即\(\{ k|\text{LCP(k,i)}\geq \text{length}_k \}\)
那么现在的问题就转变成了一个非常经典的问题,给出\(m\)个询问,每次询问区间\([l,r]\)的颜色数。
这个问题直接离线树状数组即可。(将询问按右端点排序,然后处理出\(pre\)数组表示当前位置的数上一次出现的位置,每次在树状数组中加入当前数的同时删掉该数的\(pre\),就可以正确得到区间颜色数了)。

对于第二个问题,和第一个问题本质基本相同,转化为求每个点被多少个询问区间覆盖。这个东西差分+离线+树状数组即可解决。
总复杂度是\(O(n \log n)\)的。当然这题的做法很多,有将问题转化后\(O(n \sqrt n)\)莫队求解的,也有直接大力上AC自动机的。不过目前我会的只有后缀数组相关做法...

#include 
using namespace std;

const int N = 500010;

int a[N], n, m, len, bl[N], T = 10001, length[N], st[N], L[N];
int sa[N], height[N], tong[N], rnk[N], tp[N], ans[N], pre[N];
struct Q {int l, r, id;} q[N];
bool cmp(Q a, Q b) {return a.r < b.r;}

void radix_sort() {
	for(int i = 1; i <= m; ++i) tong[i] = 0;
	for(int i = 1; i <= len; ++i) tong[rnk[i]]++;
	for(int i = 1; i <= m; ++i) tong[i] += tong[i - 1];
	for(int i = len; i; --i) sa[tong[rnk[tp[i]]]--] = tp[i];
}

void suffix_sort() {
	for(int i = 1; i <= len; ++i) rnk[i] = a[i], tp[i] = i;
	m = T; radix_sort();
	for(int w = 1, p = 1; p < len && w <= len; w <<= 1, m = p) {
		p = 0; 
		for(int i = 1; i <= w; ++i) tp[++p] = len - w + i;
		for(int i = 1; i <= len; ++i) if(sa[i] > w) tp[++p] = sa[i] - w;
		radix_sort(); swap(tp, rnk); rnk[sa[1]] = p = 1;
		for(int i = 2; i <= len; ++i) 
			rnk[sa[i]] = (tp[sa[i]] == tp[sa[i - 1]] && tp[sa[i] + w] == tp[sa[i - 1] + w]) ? p : ++p;
	}
	for(int i = 1, k = 0; i <= len; ++i) {
		if(k) --k;
		int j = sa[rnk[i] - 1];
		while(a[i + k] == a[j + k] && i + k <= len && j + k <= len) ++k;
		height[rnk[i]] = k; 
	}
}

namespace ST {
int f[N][25], LG[N];
int query(int l, int r) {
	int k = LG[r - l + 1];
	return min(f[l][k], f[r - (1 << k) + 1][k]);
}
void init() {
	for(int i = 1; i <= len; ++i) f[i][0] = height[i];
	for(int i = 2; i <= len; ++i) LG[i] = LG[i >> 1] + 1;
	for(int j = 1; j <= 24; ++j) 
		for(int i = 1; i + (1 << j) - 1 <= len; ++i) 
			f[i][j] = min(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]);
}
}

namespace BIT {
int c[N];
#define lowbit(i) (i & -i)
void clear() {memset(c, 0, sizeof(c));}
void add(int x, int v) {
	if(!x) return;
	for(int i = x; i <= len; i += lowbit(i)) c[i] += v;
}
int query(int x) {
	if(!x) return 0;
	int ans = 0;
	for(int i = x; i; i -= lowbit(i)) ans += c[i];
	return ans;
}
}

int main() {
#ifndef ONLINE_JUDGE
	freopen("1.in", "r", stdin);
	freopen("qwq.out", "w", stdout);
#endif
	int M; scanf("%d%d", &n, &M);
	for(int i = 1; i <= n; ++i) {
		int x, tmp; scanf("%d", &x);
		while(x--) {
			scanf("%d", &tmp); 
			a[++len] = tmp; bl[len] = i;
		}
		a[++len] = ++T;
		scanf("%d", &x);
		while(x--) {
			scanf("%d", &tmp);
			a[++len] = tmp; bl[len] = i; 
		}
		a[++len] = ++T;
	}
	for(int i = 1; i <= M; ++i) {
		int x, tmp; scanf("%d", &x);
		length[i] = x;
		st[i] = len + 1;
		while(x--) {
			scanf("%d", &tmp);
			a[++len] = tmp;
		}
		a[++len] = ++T;
	}
	suffix_sort(); ST::init();
	for(int i = 1; i <= M; ++i) {
		int l = rnk[st[i]], r = len;
		q[i].l = q[i].r = rnk[st[i]];
		while(l <= r) {
			int mid = (l + r) >> 1;
			if(ST::query(rnk[st[i]] + 1, mid) >= length[i]) l = mid + 1, q[i].r = mid;
			else r = mid - 1;
		}
		l = 1, r = rnk[st[i]];
		while(l <= r) {
			int mid = (l + r) >> 1;
			if(ST::query(mid + 1, rnk[st[i]]) >= length[i]) r = mid - 1, q[i].l = mid;
			else l = mid + 1;
		}
		q[i].id = i;
	}
	memset(tong, 0, sizeof(tong));
	for(int i = 1; i <= len; ++i) {
		if(bl[sa[i]] > 0) {
			pre[i] = tong[bl[sa[i]]];
			tong[bl[sa[i]]] = i;
		}
	}
	sort(q + 1, q + M + 1, cmp);
	int cur = 1; BIT::clear(); 
	for(int i = 1; i <= len; ++i) {
		if(bl[sa[i]]) {BIT::add(pre[i], -1); BIT::add(i, 1);}
		while(cur <= M && q[cur].r == i) {
			ans[q[cur].id] = BIT::query(q[cur].r) - BIT::query(q[cur].l - 1);
			++cur;
		}
	}
	for(int i = 1; i <= M; ++i) printf("%d\n", ans[i]);
	memset(ans, 0, sizeof(ans));
	for(int i = 1; i <= M; ++i) L[i] = q[i].l;
	sort(L + 1, L + M + 1); cur = 1; int k = 1; 
	BIT::clear();
	for(int i = 1; i <= len; ++i) {
		while(cur <= M && L[cur] == i) BIT::add(i, 1), ++cur;
		if(bl[sa[i]] > 0) ans[bl[sa[i]]] += BIT::query(i) - BIT::query(pre[i]);
		while(k <= M && q[k].r == i) BIT::add(q[k++].l, -1);
	}
	for(int i = 1; i <= n; ++i) printf("%d ", ans[i]); puts("");
	return 0;
}

你可能感兴趣的:(BZOJ2754: [SCOI2012]喵星球上的点名)