传送门

不知道为啥写这题时的感觉跟以前写「阿狸的打字机」很像。。。

第一问后缀数组常规操作,给定模式串和母串,求每个模式串在多少个母串出现过。

把所有串染色后拼起来(姓和名之间也要加上特殊字符),建出$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]);
}