题意
我们将所有姓名串和点名串拼在一起,中间用原串中不会出现的数隔开,对每个点记录它是哪个串的。
先考虑第一问:
考虑如果点名串(s1)是某个姓名串(s2)的子串,则(s1)必定和(s2)的一个后缀(s2')的(lcp)为(s1)的长度,即:(lcp(s1,s2')=len(s1))。
对于一个点名串,以它作为前缀的后缀在后缀排序后必定是一个区间,我们可以二分得出,设为([l,r]),我们接下来就要统计([l,r])内出现的不同的姓名的个数,这是个经典问题,可以用(HH)的项链的的方法离线解决。
再考虑第二问:
我们可以算出每个点被多少询问区间覆盖,但这样会计重。
我们从(1)到(n)((n)是串长)扫描所有位置,同时维护可以对这个位置产生贡献的区间。具体说就是对于([l,r]),我们在扫到(l)时它开始产生贡献,我们对([l,r])区间(+1),在扫到(r+1)时,它不再产生贡献,我们对([l,r])区间(-1),这个可以用差分实现。
现在考虑怎么算出当前位置(i)真正的贡献,即算出只包含(i),而不包含(i)所在的原串的其他点的区间。我们找到(last_{pos_i}),其中(pos_i)表示(i)所在原串,(last_{pos_i})表示上一个属于(pos_i)这个原串的位置,那么答案就是(sum_i-sum_{last_{pos_i}})。
code:
#include<bits/stdc++.h>
using namespace std;
const int maxm=5*1e4+10;
const int maxQ=1e5+10;
const int maxl=1e6+10;
int n,m,Q,num;
int a[maxl],len[maxl],firpos[maxl],pos[maxl],last[maxm],ans1[maxQ],ans2[maxm];
int sa[maxl],rk[maxl],oldrk[maxl],id[maxl],tmpid[maxl],cnt[maxl],lg[maxl];
int height[maxl][20];
struct Query{int l,r,id;}qr[maxQ];
inline void reads(int now)
{
firpos[now]=n+1;
scanf("%d",&len[now]);
for(int i=1;i<=len[now];i++)scanf("%d",&a[++n]),pos[n]=now;
a[++n]=now+10000;
}
inline bool cmp(Query x,Query y){return x.r<y.r;}
inline bool check(int x,int y,int k){return oldrk[x]==oldrk[y]&&oldrk[x+k]==oldrk[y+k];}
inline void SA_build()
{
num=500000;
for(int i=1;i<=n;i++)cnt[rk[i]=a[i]]++;
for(int i=1;i<=num;i++)cnt[i]+=cnt[i-1];
for(int i=n;i;i--)sa[cnt[rk[i]]--]=i;
for(int t=1;t<=n;t<<=1)
{
int tot=0;
for(int i=n-t+1;i<=n;i++)id[++tot]=i;
for(int i=1;i<=n;i++)if(sa[i]>t)id[++tot]=sa[i]-t;
tot=0;
memset(cnt,0,sizeof(cnt));
for(int i=1;i<=n;i++)cnt[tmpid[i]=rk[id[i]]]++;
for(int i=1;i<=num;i++)cnt[i]+=cnt[i-1];
for(int i=n;i;i--)sa[cnt[tmpid[i]]--]=id[i];
memcpy(oldrk,rk,sizeof(rk));
for(int i=1;i<=n;i++)rk[sa[i]]=check(sa[i-1],sa[i],t)?tot:++tot;
num=tot;
}
for(int i=1,j=0;i<=n;i++)
{
if(j)j--;
while(a[i+j]==a[sa[rk[i]-1]+j])j++;
height[rk[i]][0]=j;
}
for(int j=1;j<=18;j++)
for(int i=1;i+(1<<j)-1<=n;i++)
height[i][j]=min(height[i][j-1],height[i+(1<<(j-1))][j-1]);
}
inline int query(int l,int r)
{
l++;
int t=lg[r-l+1];
return min(height[l][t],height[r-(1<<t)+1][t]);
}
inline int findl(int id)
{
int l=1,r=rk[firpos[id]]-1,res=rk[firpos[id]];
while(l<=r)
{
int mid=(l+r)>>1;
if(query(mid,rk[firpos[id]])<len[id])l=mid+1;
else res=mid,r=mid-1;
}
return res;
}
inline int findr(int id)
{
int l=rk[firpos[id]]+1,r=n,res=rk[firpos[id]];
while(l<=r)
{
int mid=(l+r)>>1;
if(query(rk[firpos[id]],mid)<len[id])r=mid-1;
else res=mid,l=mid+1;
}
return res;
}
struct Tree_arry
{
#define lowbit(x) (x&-x)
int a[maxl];
inline void clear(){memset(a,0,sizeof(a));}
inline void add(int x,int k){for(int i=x;i<=n;i+=lowbit(i))a[i]+=k;}
inline int query(int x){int res=0;for(int i=x;i;i-=lowbit(i))res+=a[i];return res;}
}tr;
int main()
{
lg[0]=-1;
for(int i=1;i<=1e6;i++)lg[i]=lg[i>>1]+1;
scanf("%d%d",&m,&Q);
for(int i=1;i<=m;i++)reads(2*i-1),reads(2*i);
for(int i=1;i<=Q;i++)reads(2*m+i);
SA_build();
for(int i=1;i<=Q;i++)qr[i].l=findl(2*m+i),qr[i].r=findr(2*m+i),qr[i].id=i;
sort(qr+1,qr+Q+1,cmp);
for(int i=1,j=1;i<=Q;i++)
{
while(j<=qr[i].r)
{
if(pos[sa[j]]<=2*m)
{
if(last[(pos[sa[j]]+1)>>1])tr.add(last[(pos[sa[j]]+1)>>1],-1);
last[(pos[sa[j]]+1)>>1]=j;
tr.add(j,1);
}
j++;
}
ans1[qr[i].id]=tr.query(qr[i].r)-tr.query(qr[i].l-1);
}
for(int i=1;i<=m;i++)last[i]=0;
tr.clear();
for(int i=1;i<=Q;i++)tr.add(qr[i].l,1);
for(int i=1,j=1;i<=n;i++)
{
while(j<=Q&&qr[j].r<i)tr.add(qr[j].l,-1),j++;
if(pos[sa[i]]<=2*m)
{
ans2[(pos[sa[i]]+1)>>1]+=tr.query(i)-tr.query(last[(pos[sa[i]]+1)>>1]);
last[(pos[sa[i]]+1)>>1]=i;
}
}
for(int i=1;i<=Q;i++)printf("%d
",ans1[i]);
for(int i=1;i<=m;i++)printf("%d ",ans2[i]);
return 0;
}