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

时间:2023-03-09 00:15:09
P2336 [SCOI2012]喵星球上的点名(后缀自动机+莫队+dfs序)

P2336 [SCOI2012]喵星球上的点名

名字怎么存?显然是后缀自动机辣

询问点到多少个喵喵喵其实就是

查询后缀自动机上parent树的一个子树

于是我们考虑莫队

怎么树上莫队呢

我们用dfs序处理后缀自动机上的parent树

把parent树映射到序列上

于是我们就可以愉快地莫队辣

最后怎么处理每个喵喵喵被点到的次数呢

我们在莫队的时候维护一个$Ls[i]$数组维护颜色$i$的存在时间(显然会被划分为好几段)

当颜色$i$第一次进队(队内本来没有该颜色)时,就记下$Ls[i]$

当队中最后一个颜色$i$出队时,答案就累加上$now-Ls[i]$,即为颜色$i$在这段的存在时间

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<map>
#define rint register int
using namespace std;
int read(){
char c=getchar();int x=;
while(c<''||c>'') c=getchar();
while(''<=c&&c<='') x=x*+(c^),c=getchar();
return x;
}
#define N 200005
int n,m,Len,L,R,dfn[N],siz[N],Co[N];
int tt,in[N],Ls[N],ans1[N],ans2[N],tot;
int cnt,hd[N],nxt[N],ed[N],poi[N];
struct data{int l,r,Id;}a[N];
inline bool cmp(data A,data B){
return (A.l/Len==B.l/Len)?A.r<B.r:A.l/Len<B.l/Len;
}
inline void adde(int x,int y){
nxt[ed[x]]=++cnt, hd[x]=hd[x]?hd[x]:cnt,
ed[x]=cnt, poi[cnt]=y;
}
struct Sam{
int fa[N],len[N],col[N],ed,q,Clock;
Sam(){Clock=;ed=;}
map<int,int> mp[N];
int add(int p,int x,int id){
len[++ed]=len[p]+; col[ed]=id;
for(;p&&!mp[p][x];p=fa[p]) mp[p][x]=ed;
if(!p) {fa[ed]=;return ed;}
q=mp[p][x];
if(len[q]==len[p]+){fa[ed]=q;return ed;}
len[++ed]=len[p]+; mp[ed]=mp[q];
fa[ed]=fa[q]; fa[q]=fa[ed-]=ed;
for(;mp[p][x]==q;p=fa[p]) mp[p][x]=ed;
return ed-;
}
void link(){for(rint i=;i<=ed;++i) adde(fa[i],i);}
void dfs(int x){//dfs序把parent树映射到序列上
dfn[x]=++Clock; Co[Clock]=col[x]; siz[x]=;
for(int i=hd[x];i;i=nxt[i])
dfs(poi[i]),siz[x]+=siz[poi[i]];
}
}S;
int main(){
n=read();m=read();int q,now;
for(rint i=;i<=n;++i){
q=read(); now=;
while(q--) now=S.add(now,read(),i);
q=read(); now=;
while(q--) now=S.add(now,read(),i);
}S.link(); S.dfs(); Len=sqrt(S.ed);
for(rint i=;i<=m;++i){
q=read();now=;
while(q--) now=S.mp[now][read()];
if(!now) continue;
a[++tt]=(data){dfn[now],dfn[now]+siz[now]-,i};
}
if(tt){//在映射序列上进行莫队
sort(a+,a+tt+,cmp);
for(rint i=a[].l;i<=a[].r;++i){
if(!in[Co[i]]&&Co[i]) ++tot,Ls[Co[i]]=;
++in[Co[i]];
}ans1[a[].Id]=tot; L=a[].l; R=a[].r;
for(rint i=;i<=tt;++i){
while(L<a[i].l){
--in[Co[L]];
if(!in[Co[L]]&&Co[L]) --tot,ans2[Co[L]]+=i-Ls[Co[L]];
++L;
}
while(R>a[i].r){
--in[Co[R]];
if(!in[Co[R]]&&Co[R]) --tot,ans2[Co[R]]+=i-Ls[Co[R]];
--R;
}
while(L>a[i].l){
--L;
if(!in[Co[L]]&&Co[L]) ++tot,Ls[Co[L]]=i;
++in[Co[L]];
}
while(R<a[i].r){
++R;
if(!in[Co[R]]&&Co[R]) ++tot,Ls[Co[R]]=i;
++in[Co[R]];
}
ans1[a[i].Id]=tot;
}
while(L<=R){
--in[Co[L]];
if(!in[Co[L]]&&Co[L]) --tot,ans2[Co[L]]+=tt-Ls[Co[L]]+;
++L;
}//注意最后剩下的数据要算进去
}
for(rint i=;i<=m;++i) printf("%d\n",ans1[i]);
for(rint i=;i<=n;++i) printf("%d ",ans2[i]);
return ;
}