不知道为啥写这题时的感觉跟以前写「阿狸的打字机」很像。。。
第一问后缀数组常规操作,给定模式串和母串,求每个模式串在多少个母串出现过。
把所有串染色后拼起来(姓和名之间也要加上特殊字符),建出$SA$,$ST$表+二分出匹配区间后就成了HH的项链。这里选择莫队解决。
关键是第二问,询问每种颜色被多少个询问区间覆盖。
这里就是莫队的一个小技巧了:如果一种颜色在询问$q_L$里出现,又在$q_R$里消失,那么在$q_{L\sim R-1}$这种颜色都出现了,答案就加上$R-L$,一波作差就好了。
然而蒟蒻莫队并没怎么练过(板子都打不对)。。。然后死在了第二问的莫队常规操作上$QAQ$
这也太技巧了。——摘自《潮语·潮讽》
代码:(如果蒟蒻写了个假了的莫队、$SA$或$ST$表请各位$dalao$指正。。。)
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#define maxn 500005
#define inf 0x3f3f3f3f
using namespace std;
inline int read(){
int x=0,y=0;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')y=1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
return y?-x:x;
}
int pos[maxn],col[maxn],tax[maxn],tp[maxn],rk[maxn],sa[maxn],s[maxn],hei[maxn],lg[maxn],f[maxn][25],ans1[maxn],ans2[maxn],len[maxn],cnt[maxn],all,n,m,N,M;
struct Question{
int b,l,r,num;
bool operator < (const Question &x)const{
if(b!=x.b)return b<x.b;
if(b&1)return r<x.r;
return r>x.r;
}
}q[maxn];
void Rsort(){
for(register int i=0;i<=m;++i)tax[i]=0;
for(register int i=1;i<=n;++i)++tax[rk[i]];
for(register int i=1;i<=m;++i)tax[i]+=tax[i-1];
for(register int i=n;i;--i)sa[tax[rk[tp[i]]]--]=tp[i];
}
void Ssort(){
for(register int i=1;i<=n;++i)rk[i]=s[i]+1,m=max(m,s[i]+1),tp[i]=i;
Rsort();
for(register int k=1,p=0;p<n;m=p,k<<=1){
p=0;
for(register int i=1;i<=k;++i)tp[++p]=n-k+i;
for(register int i=1;i<=n;++i)if(sa[i]>k)tp[++p]=sa[i]-k;
Rsort();
for(register int i=1;i<=n;++i)tp[i]=rk[i];
rk[sa[1]]=p=1;
for(register int i=2;i<=n;++i)
rk[sa[i]]=tp[sa[i]]==tp[sa[i-1]]&&tp[sa[i]+k]==tp[sa[i-1]+k]?p:++p;
}
}
void get_height(){
int k=0,x;
for(register int i=1;i<=n;++i){
if(rk[i]==1)continue;
if(k)--k;
x=sa[rk[i]-1];
while(i+k<=n&&x+k<=n&&s[i+k]==s[x+k])++k;
f[rk[i]][0]=hei[rk[i]]=k;
}
}
void ST(){
for(register int i=1;i<=n;++i)lg[i]=lg[i-1]+(1<<lg[i-1]==i);
for(register int j=1;j<lg[n];++j)
for(register int i=1;i+(1<<(j-1))<=n;++i)
f[i][j]=min(f[i][j-1],f[i+(1<<(j-1))][j-1]);
}//st表预处理
inline int query(int l,int r){
int len=lg[r-l+1]-1;
return min(f[l][len],f[r-(1<<len)+1][len]);
}//st表查询
inline int lcp(int i,int j){
if(i==j)return n-sa[i]+1;
return query(min(i,j)+1,max(i,j));
}
void work(int x){
int ra=rk[pos[x]],l=1,r=ra,mid,ans=ra;
while(l<r){
mid=l+r>>1;
if(lcp(mid,ra)>=len[x])r=mid;
else l=mid+1;
}//二分左端点
q[x].l=l;
l=ra,r=n;
while(l<=r){
mid=l+r>>1;
if(lcp(mid,ra)>=len[x])l=mid+1,ans=mid;
else r=mid-1;
}//二分右端点
q[x].r=ans,q[x].num=x;
}
inline void Get(int &l,int x=0){
l=read();
for(register int i=1;i<=l;++i)
s[i+n]=read()+1,col[i+n]=x;
s[n+l+1]=0;
n+=l+1;
}//读入
int p,k;//k是当前询问编号
inline void add(int x){
p=col[sa[x]];
if(p){
if(!cnt[p])++all,ans2[p]-=k;
++cnt[p];
}
}
inline void del(int x){
p=col[sa[x]];
if(p){
--cnt[p];
if(!cnt[p])--all,ans2[p]+=k;
}
}
int main(){
N=read(),M=read();
int l=1,r=0,sq=sqrt(M);
for(register int i=1;i<=N;++i)
Get(len[0],i),Get(len[0],i);
for(register int i=1;i<=M;++i)
pos[i]=n+1,Get(len[i]);
Ssort(),get_height(),ST();
for(register int i=1;i<=M;++i)
work(i);
for(register int i=1;i<=M;++i)
q[i].b=q[i].l/sq+1;
sort(q+1,q+1+M);
while(++k<=M){
while(l<q[k].l)del(l++);
while(l>q[k].l)add(--l);
while(r<q[k].r)add(++r);
while(r>q[k].r)del(r--);
ans1[q[k].num]=all;
}
while(l<=r)del(l++);
for(register int i=1;i<=M;++i)
printf("%d\n",ans1[i]);
for(register int i=1;i<=N;++i)
printf("%d ",ans2[i]);
}