P2336 [SCOI2012]喵星球上的点名(后缀自动机+莫队+dfs序)
名字怎么存?显然是后缀自动机辣
询问点到多少个喵喵喵其实就是
查询后缀自动机上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=0; while(c<'0'||c>'9') c=getchar(); while('0'<=c&&c<='9') x=x*10+(c^48),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=0;ed=1;} map<int,int> mp[N]; int add(int p,int x,int id){ len[++ed]=len[p]+1; col[ed]=id; for(;p&&!mp[p][x];p=fa[p]) mp[p][x]=ed; if(!p) {fa[ed]=1;return ed;} q=mp[p][x]; if(len[q]==len[p]+1){fa[ed]=q;return ed;} len[++ed]=len[p]+1; mp[ed]=mp[q]; fa[ed]=fa[q]; fa[q]=fa[ed-1]=ed; for(;mp[p][x]==q;p=fa[p]) mp[p][x]=ed; return ed-1; } void link(){for(rint i=1;i<=ed;++i) adde(fa[i],i);} void dfs(int x){//dfs序把parent树映射到序列上 dfn[x]=++Clock; Co[Clock]=col[x]; siz[x]=1; 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=1;i<=n;++i){ q=read(); now=1; while(q--) now=S.add(now,read(),i); q=read(); now=1; while(q--) now=S.add(now,read(),i); }S.link(); S.dfs(1); Len=sqrt(S.ed); for(rint i=1;i<=m;++i){ q=read();now=1; while(q--) now=S.mp[now][read()]; if(!now) continue; a[++tt]=(data){dfn[now],dfn[now]+siz[now]-1,i}; } if(tt){//在映射序列上进行莫队 sort(a+1,a+tt+1,cmp); for(rint i=a[1].l;i<=a[1].r;++i){ if(!in[Co[i]]&&Co[i]) ++tot,Ls[Co[i]]=1; ++in[Co[i]]; }ans1[a[1].Id]=tot; L=a[1].l; R=a[1].r; for(rint i=2;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]]+1; ++L; }//注意最后剩下的数据要算进去 } for(rint i=1;i<=m;++i) printf("%d\n",ans1[i]); for(rint i=1;i<=n;++i) printf("%d ",ans2[i]); return 0; }