[luoguP2336] [SCOI2012]喵星球上的点名(后缀数组 + 暴力)

传送门

 

原本的想法是把所有的串不管是名字还是询问都连起来,记录一下询问串在sa数组中的位置

对于每个询问可以在sa数组中二分出左右边界,第一问用莫队,第二问差分乱搞。

结果发现我差分的思路想错了,先写了一个暴力,二分出左右边界之后直接从l枚举到r,想试试正确性,就交了一遍,结果过了。。。。

因为是暴力,可以不用二分左右边界,直接向左向右枚举扩展就可以了,st表也不用了,但我懒得改了

 

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#define N 2000001

using namespace std;

int n, m, cnt, mx, len;
int M[N], x[N], y[N], s[N], sa[N], b[N], d[N][21], Rank[N], Log[N], height[N], id[N], pos[N], ans[N], num[N];

inline int read()
{
	int x = 0, f = 1;
	char ch = getchar();
	for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = -1;
	for(; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + ch - '0';
	return x * f;
}

inline void build_sa()
{
	int i, k, p;
	for(i = 0; i < mx; i++) b[i] = 0;
	for(i = 0; i < len; i++) b[x[i] = s[i]]++;
	for(i = 1; i < mx; i++) b[i] += b[i - 1];
	for(i = len - 1; i >= 0; i--) sa[--b[x[i]]] = i;
	for(k = 1; k <= len; k <<= 1)
	{
		p = 0;
		for(i = len - k; i < len; i++) y[p++] = i;
		for(i = 0; i < len; i++) if(sa[i] >= k) y[p++] = sa[i] - k;
		for(i = 0; i < mx; i++) b[i] = 0;
		for(i = 0; i < len; i++) b[x[y[i]]]++;
		for(i = 1; i < mx; i++) b[i] += b[i - 1];
		for(i = len - 1; i >= 0; i--) sa[--b[x[y[i]]]] = y[i];
		swap(x, y);
		p = 1, x[sa[0]] = 0;
		for(i = 1; i < len; i++)
			x[sa[i]] = y[sa[i - 1]] == y[sa[i]] && y[sa[i - 1] + k] == y[sa[i] + k] ? p - 1 : p++;
		if(p >= len) break;
		mx = p;
	}
}

inline void build_height()
{
	int i, j, k = 0;
	for(i = 0; i < len; i++) Rank[sa[i]] = i;
	for(i = 0; i < len; i++)
	{
		if(k) k--;
		j = sa[Rank[i] - 1];
		while(s[i + k] == s[j + k]) k++;
		height[Rank[i]] = k;
	}
}

inline void build_st()
{
	int i, j;
	for(i = 1; i < len; i++)
	{
		Log[i] = log2(i);
		d[i][0] = height[i];
	}
	Log[i] = log2(i);
	for(j = 1; (1 << j) < len; j++)
		for(i = 1; i + (1 << j) - 1 < len; i++)
			d[i][j] = min(d[i][j - 1], d[i + (1 << j - 1)][j - 1]);
}

inline int query(int l, int r)
{
	int t = Log[r - l + 1];
	return min(d[l][t], d[r - (1 << t) + 1][t]);
}

inline int search2(int p, int llen)
{
	int r = p, l = 1, mid;
	while(l <= r)
	{
		mid = (l + r) >> 1;
		if(query(mid, p) >= llen) r = mid - 1;
		else l = mid + 1;
	}
	return r;
}

inline int search1(int p, int llen)
{
	p++;
	int l = p, r = len - 1, mid;
	while(l <= r)
	{
		mid = (l + r) >> 1;
		if(query(p, mid) >= llen) l = mid + 1;
		else r = mid - 1;
	}
	return l - 1;
}

inline void solve()
{
	int i, j, l, r;
	for(i = 1; i <= m; i++)
	{
		r = j = search1(Rank[id[i]], M[i]);
		l = j = search2(Rank[id[i]], M[i]);
		for(j = l; j <= r; j++)
			if(!num[pos[sa[j]]]++ && pos[sa[j]]) cnt++, ans[pos[sa[j]]]++;
		printf("%d\n", cnt);
		cnt = 0;
		for(j = l; j <= r; j++) num[pos[sa[j]]] = 0;
	}
	for(i = 1; i <= n; i++) printf("%d ", ans[i]);
}

int main()
{
	int i, j, t;
	n = read();
	m = read();
	for(i = 1; i <= n; i++)
		for(j = 1; j <= 2; j++)
		{
			t = read();
			while(t--)
			{
				pos[len] = i;
				s[len++] = read() + 100000;
			}
			s[len++] = mx++;
		}
	for(i = 1; i <= m; i++)
	{
		M[i] = read();
		id[i] = len;
		for(j = 1; j <= M[i]; j++) s[len++] = read() + 100000;
		s[len++] = mx++;
	}
	mx = 120000;
	build_sa();
	build_height();
	build_st();
	solve();
	return 0;
}

  

posted @ 2018-01-15 14:40  zht467  阅读(255)  评论(0编辑  收藏  举报