洛谷P2336 [SCOI2012]喵星球上的点名(SA+莫队)

Posted ListenSnow

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了洛谷P2336 [SCOI2012]喵星球上的点名(SA+莫队)相关的知识,希望对你有一定的参考价值。

原题链接

题意

\\(N\\) 只喵,每只喵有一个名和一个姓。

\\(M\\) 次点名,如果一只喵的名或姓中包含这个字符串,这只喵就会喊”到“。

求:

  • 对于每次点名,有多少只喵喊”到“。

  • 每只喵一共被喊到多少次”到“。

\\(1 \\leq n\\le 5 \\times 10^4\\)\\(1 \\leq m \\le 10^5\\),喵星人的名字总长和点名串的总长分别不超过 \\(10^5\\),保证喵星人的字符串中作为字符存在的数不超过 \\(10^4\\)

思路

对于每次点名,实质上就是询问当前字符串是多少喵的字符串的字串。由于总名字的长度不会超过 \\(10^5\\),所以可以考虑将所有姓和名拼接在一起,中间用特殊符号隔开,进行一次后缀排序。

根据一个字符串的所有字串可以用它所有后缀的所有前缀来表示,每次询问都可以在后缀数组上二分找到前缀包含当前询问串的区间。那么对于这个区间内所有的后缀,它所对应的原串就是被点名到的喵。注意到区间移动更新答案是 \\(O(1)\\) 的,于是可以考虑莫队。

而对于每只喵一共被喊到多少次,只需要在莫队移动端点的时候,分别加上(减去)当前字符串进入莫队(离开莫队)的时间即可。

code:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
const int N=1e5+10,B=405;
int height[N],sa[N<<1],rk[N],x[N<<1],y[N<<1],cnt[N<<1],yyx=10000;
int cat[N],ans[N],a[N<<1],id[N<<1],blk[N<<1];
void get_sa(int n,int m)

	for(int i=1;i<=n;i++) cnt[x[i]=a[i]]++;
	for(int i=1;i<=m;i++) cnt[i]+=cnt[i-1];
	for(int i=n;i>=1;i--) sa[cnt[x[i]]--]=i;
	for(int k=1;k<=n;k<<=1)
	
		int num=0;
		for(int i=n-k+1;i<=n;i++) y[++num]=i;
		for(int i=1;i<=n;i++) if(sa[i]>k) y[++num]=sa[i]-k;
		for(int i=1;i<=m;i++) cnt[i]=0;
		for(int i=1;i<=n;i++) cnt[x[i]]++;
		for(int i=1;i<=m;i++) cnt[i]+=cnt[i-1];
		for(int i=n;i>=1;i--) sa[cnt[x[y[i]]]--]=y[i],y[i]=0;
		swap(x,y);num=1;x[sa[1]]=1;
		for(int i=2;i<=n;i++) x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k])?num:++num;
		if(num==n) break;m=num;
	

int res,New;
struct Queryint l,r,id;q[N];
bool cmp(Query x,Query y)return id[x.l]==id[y.l]?x.r<y.r:x.l<y.l;
void add(int x,int cur)if(++cnt[id[x]]==1) res++,ans[id[x]]+=New-cur+1;
void del(int x,int cur)if(--cnt[id[x]]==0) res--,ans[id[x]]-=New-cur+1;
int main()

//	freopen("10.in","r",stdin);
//	freopen("1.out","w",stdout);
	int n,m;scanf("%d%d",&n,&m);int tot=0;
	for(int i=1;i<=n;i++) for(int k=0;k<2;k++)
	
		int len;scanf("%d",&len);for(int j=1;j<=len;j++) scanf("%d",&a[++tot]),id[tot]=i;a[++tot]=++yyx;
	
	for(int i=1;i<=tot;i++) blk[i]=(i-1)/B+1;get_sa(tot,yyx);
	for(int i=1;i<=m;i++)
	
		int len,x;scanf("%d",&len);int L=1,R=tot;
		for(int j=1;j<=len;j++)
		
			scanf("%d",&x);int l=L,r=R;
			while(l<=r)
			
				int mid=l+r>>1;
				if(a[sa[mid]+j-1]<x) l=mid+1;
				else r=mid-1;
			
			int temp=l;l=L,r=R;
			while(l<=r)
			
				int mid=l+r>>1;
				if(a[sa[mid]+j-1]<=x) l=mid+1;
				else r=mid-1;
			
			L=temp,R=r;
		
		if(L<=R) q[++New]=QueryL,R,i;
	
	sort(q+1,q+New+1,cmp);memset(cnt,0,sizeof(cnt));
	for(int l=1,r=0,k=1;k<=New;k++)
	
		while(l>q[k].l) add(sa[--l],k);
		while(r<q[k].r) add(sa[++r],k);
		while(l<q[k].l) del(sa[l++],k);
		while(r>q[k].r) del(sa[r--],k);
		cat[q[k].id]=res;
	
    for(int i=1;i<=m;i++) printf("%d\\n",cat[i]);
    for(int i=1;i<=n;i++) printf("%d ",ans[i]);
	return 0;

以上是关于洛谷P2336 [SCOI2012]喵星球上的点名(SA+莫队)的主要内容,如果未能解决你的问题,请参考以下文章

P2336 [SCOI2012]喵星球上的点名(后缀自动机+莫队)

luogu P2336 [SCOI2012]喵星球上的点名

bzoj2754SCOI2012喵星球上的点名

BZOJ 2754: [SCOI2012]喵星球上的点名

bzoj2754 [SCOI2012]喵星球上的点名 (后缀数组+树状数组)

BZOJ 2754: [SCOI2012]喵星球上的点名