传送门
思路:可以用无脑的后缀数组+单调栈维护,当然也可以选择
s
a
m
sam
sam+虚树。
代码:
#include<bits/stdc++.h>
#define ri register int
using namespace std;
typedef long long ll;
const ll mod=23333333333333333,N=1e6+5;
inline int read(){
#define gc getchar
int ans=0;
char ch=gc();
while(!isdigit(ch))ch=gc();
while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
return ans;
#undef gc
}
char s[N];
namespace SA{
int ht[N],rk[N],sa[N],sa2[N],st[N][20],Lg[N],pos[N],a[N],q[N],top,L[N],R[N],n,m;
inline void Sort(){
static int cnt[N];
for(ri i=1;i<=m;++i)cnt[i]=0;
for(ri i=1;i<=n;++i)++cnt[rk[i]];
for(ri i=2;i<=m;++i)cnt[i]+=cnt[i-1];
for(ri i=n;i;--i)sa[cnt[rk[sa2[i]]]--]=sa2[i];
}
inline void getsa(){
n=strlen(s+1);
for(ri i=1;i<=n;++i)rk[i]=s[i]-'a'+1,sa2[i]=i;
m=127,Sort();
for(ri w=1,p=0;m^n;p=0,w<<=1){
for(ri i=n-w+1;i<=n;++i)sa2[++p]=i;
for(ri i=1;i<=n;++i)if(sa[i]>w)sa2[++p]=sa[i]-w;
Sort(),swap(sa2,rk);
rk[sa[1]]=p=1;
for(ri i=2;i<=n;++i)rk[sa[i]]=(sa2[sa[i]]==sa2[sa[i-1]]&&sa2[sa[i]+w]==sa2[sa[i-1]+w])?p:++p;
m=p;
}
for(ri i=1,k=0,j;i<=n;ht[rk[i++]]=k)for(k?--k:k,j=sa[rk[i]-1];s[i+k]==s[j+k];++k);
for(ri i=2;i<=n;++i)Lg[i]=Lg[i>>1]+1;
for(ri i=1;i<=n;++i)st[i][0]=ht[i];
for(ri j=1;j<20;++j)for(ri i=1;i<=n;++i)st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]);
}
inline int query(int l,int r){
int k=Lg[r-l+1];
return min(st[l][k],st[r-(1<<k)+1][k]);
}
inline ll solve(){
m=read();
for(ri i=1;i<=m;++i)pos[i]=rk[read()];
sort(pos+1,pos+m+1),m=unique(pos+1,pos+m+1)-pos-1;
if(m<2)return 0;
for(ri i=1;i<m;++i)a[i]=query(pos[i]+1,pos[i+1]);
--m;
top=0;
for(ri i=1;i<=m;++i){
while(top&&a[q[top]]>a[i])R[q[top--]]=i-1;
q[++top]=i;
}
while(top)R[q[top--]]=m;
for(ri i=m;i;--i){
while(top&&a[q[top]]>=a[i])L[q[top--]]=i+1;
q[++top]=i;
}
while(top)L[q[top--]]=1;
ll ans=0;
for(ri i=1;i<=m;++i)(ans+=(ll)(R[i]-i+1)*(i-L[i]+1)*a[i]%mod)%=mod;
return ans;
}
}
int m;
int main(){
SA::n=read(),m=read();
scanf("%s",s+1);
SA::getsa();
while(m--)cout<<SA::solve()<<'\n';
return 0;
}