(树状数组那么好写我为什么要写莫队 雾)
传送门
后缀数组就不说了···然后对于每一个询问也插入序列,他的答案是一个 l c p ≥ l e n lcp\ge len lcp≥len区间,可以二分出来,然后就变成区间内问不同数字的个数,这就是裸莫队了···
第二个问题的话可以一边莫队一边计算,记录每个数字上一次出现的位置,然后把这一段的和加上
细节好多的···因为后面询问没有用特殊数字隔开wa了好久
还有莫队排序那样写的真的好快的!
代码如下:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define N 500005
#define LL long long
using namespace std;
inline int rd(){
int x=0,f=1;char c=getchar();
while(c<'0' || c>'9') f=c=='-'?-1:1,c=getchar();
while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();
return x*f;
}
inline int min(int x,int y){return x<y?x:y;}
inline int max(int x,int y){return x>y?x:y;}
int tot,n,m,q,s[N],sa[N],rk[N],tax[N],tp[N],h[N],st[N][25];
int ans[N],a[N],b[N],siz,vis[N],lst[N];
inline void rsort(){
for(int i=1;i<=m;i++) tax[i]=0;
for(int i=1;i<=n;i++) tax[rk[i]]++;
for(int i=1;i<=m;i++) tax[i]+=tax[i-1];
for(int i=n;i;i--) sa[tax[rk[tp[i]]]--]=tp[i];
}
inline void ssort(){
for(int i=1;i<=n;i++) rk[i]=s[i],tp[i]=i;
rsort();
for(int w=1,p=0;w<=n&&p<n;w<<=1,m=p){
p=0;
for(int i=n-w+1;i<=n;i++) tp[++p]=i;
for(int i=1;i<=n;i++)
if(sa[i]>w) tp[++p]=sa[i]-w;
rsort(); swap(rk,tp);
rk[sa[1]]=p=1;
for(int i=2;i<=n;i++)
if(tp[sa[i]]==tp[sa[i-1]] && tp[min(n+1,sa[i]+w)]==tp[min(n+1,sa[i-1]+w)])
rk[sa[i]]=p;
else rk[sa[i]]=++p;
}
}
inline void get_h(){
int j,k=0;
for(int i=1;i<=n;i++){
if(k) --k;
j=sa[rk[i]-1];
while(s[j+k]==s[i+k]) k++;
h[rk[i]]=k;
}
}
inline void prework(){
for(int i=1;i<=n;i++) st[i][0]=h[i];
for(int j=1;(1<<j)<=n;j++)
for(int i=1;i+(1<<j)-1<=n;i++)
st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]);
}
inline int lcp(int l,int r){
if(l>r) swap(l,r); ++l;
int k=log2(r-l+1);
return min(st[l][k],st[r-(1<<k)+1][k]);
}
inline int findl(int pos,int len){
int l=1,r=pos-1,mid,res;
while(l<=r){
mid=l+r>>1;
if(lcp(mid,pos)>=len) res=mid,r=mid-1;
else l=mid+1;
} return res;
}
inline int findr(int pos,int len){
int l=pos+1,r=n,mid,res;
while(l<=r){
mid=l+r>>1;
if(lcp(mid,pos)>=len) l=mid+1,res=mid;
else r=mid-1;
} return res;
}
struct Que{
int l,r,id,pos;
bool operator <(const Que &x) const{
if(l/siz!=x.l/siz) return l<x.l;
if((l/siz)&1) return r<x.r;
return r>x.r;
/* if(l/siz==x.l/siz) return r<x.r;
return l<x.l;*/
}
}que[N];
inline bool cmp(Que x,Que y){return x.l<y.l||(x.l==y.l&&x.r<y.r);}
inline void solve(){
int pos,l,r,cnt=0,len;
for(int i=1;i<=q;i++){
pos=rk[que[i].pos],len=que[i+1].pos-que[i].pos-1;
if(h[pos]<len) l=pos;
else l=findl(pos,len);
if(h[pos+1]<len) r=pos;
else r=findr(pos,len);
que[i].l=l,que[i].r=r;
}
sort(que+1,que+q+1);
int L=1,R=0;
for(int i=1;i<=q;i++){
while(R<que[i].r){R++; if(++vis[b[sa[R]]]==1) cnt++,lst[b[sa[R]]]=i;}
while(L<que[i].l){if(--vis[b[sa[L]]]==0) cnt--,a[b[sa[L]]]+=i-lst[b[sa[L]]]; L++;}
while(R>que[i].r){if(--vis[b[sa[R]]]==0) cnt--,a[b[sa[R]]]+=i-lst[b[sa[R]]]; R--;}
while(L>que[i].l){L--; if(++vis[b[sa[L]]]==1) cnt++,lst[b[sa[L]]]=i;}
ans[que[i].id]=cnt-(vis[0]!=0);
}
}
int main(){
tot=rd(); q=rd(); m=10001;
for(int i=1;i<=tot;i++){
int l=rd();
while(l--) s[++n]=rd(),b[n]=i;
l=rd(); s[++n]=++m;
while(l--) s[++n]=rd(),b[n]=i; s[++n]=++m;
}
for(int i=1;i<=q;i++) {
int l=rd(); que[i].pos=n+1; que[i].id=i;
while(l--) s[++n]=rd(); s[++n]=++m;
} que[q+1].pos=n+1;
ssort(); get_h(); prework(); siz=sqrt(n);
solve();
for(int i=1;i<=q;i++) printf("%d\n",ans[i]);
for(int i=1;i<=n;i++) if(vis[i]) a[i]+=q-lst[i]+1;
for(int i=1;i<=tot;i++) printf("%d ",a[i]);
return 0;
}