luogu2336 喵星球上的点名 (SA+二分答案+树状数组)

时间:2023-03-08 16:13:52

离散化一下然后把姓名串和询问串都放一起做SA

bzoj3277串类似地,满足某一询问的后缀(就是和这个询问对应的后缀的LCP>=这个询问长度的后缀)的排名也是一个区间,把这个区间二分出来即可

现在要做的两个问题就变成了:

给定一些区间、一些点,每个点有对应的颜色(就是sa[i]对应的那个姓名串对应的人),问

1.每个区间中不同颜色的数量:同HH的项链

2.每个颜色被不同区间覆盖的数量:

  想法其实和1是类似的,扫到区间左端点的时候给a[i]++,扫到右端点的时候给a[对应左端点]--,然后每个点贡献的区间数就是pre[i]-pre[i的颜色上次出现位置],因为相当于我们每次算的是左端点在上次出现位置与现在位置之间的、右端点在现在位置之后的区间,是不重不漏的

(千万不要写出s[++j]=data[j]=rd();这种代码然后因为某些特性在某些地方A某些地方wa然后跑到讨论区去丢人)

 #include<bits/stdc++.h>
#define pa pair<int,int>
#define ll long long
using namespace std;
const int maxn=4e5+,maxm=5e4+,maxq=1e5+; inline ll rd(){
ll x=;char c=getchar();int neg=;
while(c<''||c>''){if(c=='-') neg=-;c=getchar();}
while(c>=''&&c<='') x=x*+c-'',c=getchar();
return x*neg;
} struct Node{
int l,r,i;
}que[maxq];
int NN,N,M,Q;
int data[maxn],s[maxn],bel[maxn];
int sa[maxn],rank[maxn],hei[maxn],rank1[maxn],tmp[maxn],cnt[maxn];
int st[maxn][],pos[maxq][];
int tr[maxn],L[maxn],lst[maxn],ans[maxq]; inline int lowbit(int x){return x&(-x);}
inline void add(int x,int y){
for(;x<=N;x+=lowbit(x)) tr[x]+=y;
}
inline int query(int x){
int re=;for(;x>;x-=lowbit(x)) re+=tr[x];return re;
} inline void getsa(){
int i,j=,k;
for(i=;i<=N;i++) cnt[s[i]]=;
for(i=;i<=M;i++) cnt[i]+=cnt[i-];
for(i=N;i;i--) rank[i]=cnt[s[i]]; for(k=;j!=N;k<<=){
memset(cnt,,sizeof(cnt));
for(i=;i<=N;i++) cnt[rank[i+k>N?:i+k]]++;
for(i=;i<=M;i++) cnt[i]+=cnt[i-];
for(i=N;i;i--) tmp[cnt[rank[i+k>N?:i+k]]--]=i;
memset(cnt,,sizeof(cnt));
for(i=;i<=N;i++) cnt[rank[i]]++;
for(i=;i<=M;i++) cnt[i]+=cnt[i-];
for(i=N;i;i--) sa[cnt[rank[tmp[i]]]--]=tmp[i];
memcpy(rank1,rank,sizeof(rank1));
rank[sa[]]=j=;
for(i=;i<=N;i++){
if(rank1[sa[i]]!=rank1[sa[i-]]||rank1[sa[i]+k>N?:sa[i]+k]!=rank1[sa[i-]+k>N?:sa[i-]+k]) j++;
rank[sa[i]]=j;
}M=j;
}
for(i=;i<=N;i++) sa[rank[i]]=i;
} inline void geth(){
for(int i=,j=;i<=N;i++){
if(rank[i]==) continue;
if(j) j--;
int x=sa[rank[i]-];
while(x+j<=N&&i+j<=N&&s[x+j]==s[i+j]) j++;
hei[rank[i]]=j;
}
} inline void getst(){
for(int i=N;i;i--){
st[i][]=hei[i];
for(int j=;st[i+(<<(j-))][j-];j++){
st[i][j]=min(st[i][j-],st[i+(<<(j-))][j-]);
}
}
} inline int rmq(int l,int r){
int x=log2(r-l+);
return min(st[l][x],st[r-(<<x)+][x]);
} inline void getq(int id,int p,int x){
int l0,r0,l,r;
if(hei[p+]<x) r0=p;
else{
l=p+,r=N;
while(l<=r){
int m=l+r>>;
if(rmq(p+,m)>=x) r0=m,l=m+;
else r=m-;
}
}
if(hei[p]<x) l0=p;
else{
l=,r=p;
while(l<=r){
int m=l+r>>;
if(rmq(m,p)>=x) l0=m-,r=m-;
else l=m+;
}
}
que[id].l=l0,que[id].r=r0,que[id].i=id;
} inline bool cmp(Node a,Node b){
return a.r<b.r;
} inline void solve(){
int i,j,k;
for(i=;i<=Q;i++){
getq(i,rank[pos[i][]],pos[i][]-pos[i][]+);
L[i]=que[i].l;
}sort(que+,que+Q+,cmp);
sort(L+,L+Q+); for(i=,j=;i<=N&&j<=Q;i++){
int x=bel[sa[i]];
if(x){
if(lst[x]) add(lst[x],-);
add(i,);lst[x]=i;
}
for(;que[j].r==i&&j<=Q;j++){
ans[que[j].i]=query(que[j].r)-query(que[j].l-);
}
}
for(i=;i<=Q;i++) printf("%d\n",ans[i]);
memset(ans,,sizeof(ans));
memset(lst,,sizeof(lst));
memset(tr,,sizeof(tr));
for(i=,j=,k=;i<=N;i++){
for(;j<=Q&&L[j]==i;j++) add(i,);
ans[bel[sa[i]]]+=query(i)-query(lst[bel[sa[i]]]);
lst[bel[sa[i]]]=i;
for(;k<=Q&&que[k].r==i;k++) add(que[k].l,-);
}for(i=;i<=NN;i++) printf("%d ",ans[i]);
} int main(){
//freopen("2336.in","r",stdin);
int i,j,k;
NN=N=rd(),Q=rd();
for(i=,j=;i<=N*+Q;i++){
int a=rd();
if(i>N*) pos[i-N*][]=j+,pos[i-N*][]=j+a;
for(k=;k<a;k++){
s[j]=data[++j]=rd();
if(i<=N*) bel[j]=(i+)/;
}
s[j]=data[++j]=-;
}N=j;
sort(data+,data+N+);
M=unique(data+,data+N+)-data-;
for(i=;i<=N;i++) s[i]=lower_bound(data+,data+M+,s[i])-data-;
M+=;
getsa();geth();getst();
solve();
return ;
}