洛谷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]喵星球上的点名(后缀自动机+莫队)