题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=3879
题目分析:sro popoqqq
其实这题可以用SAM+虚树来做,而且我虚树也写得不熟。但SA+单调栈实在是太方便了,就懒得再写了QAQ。
上次写SA是NOIP前的事了,差点想不起怎么写,一开始构造Height数组还出错了……其实后缀数组的原理我早忘得差不多了,只是去年省赛前集训连切27题SA,所以板子还能背出来。
这题的模数是假的,算一下你就会发现答案不爆long long,最后再模就好了
CODE:
#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<stdio.h>
#include<algorithm>
using namespace std;
const int maxn=1000100;
const int maxl=22;
const long long M=23333333333333333LL;
typedef long long LL;
int cnt[maxn];
int temp1[maxn];
int temp2[maxn];
int *X=temp1,*Y=temp2;
int ST[maxn][maxl];
int sa[maxn];
int Height[maxn];
int m;
int Node[maxn];
int lcp[maxn];
int num[maxn];
int tail;
LL sum;
int Lg[maxn];
int a[maxn];
char s[maxn];
int n,q;
LL ans;
bool Comp(int p,int q,int len)
{
return ( Y[p]==Y[q] && Y[p+len]==Y[q+len] );
}
void Da()
{
m=30;
for (int i=0; i<m; i++) cnt[i]=0;
for (int i=0; i<n; i++) cnt[ X[i]=a[i] ]++;
for (int i=1; i<m; i++) cnt[i]+=cnt[i-1];
for (int i=n-1; i>=0; i--) sa[ --cnt[ X[i] ] ]=i;
int p=1;
for (int j=1; p<n; j<<=1,m=p)
{
p=0;
for (int i=n-j; i<n; i++) Y[p++]=i;
for (int i=0; i<n; i++) if (sa[i]>=j) Y[p++]=sa[i]-j;
for (int i=0; i<m; i++) cnt[i]=0;
for (int i=0; i<n; i++) cnt[ X[ Y[i] ] ]++;
for (int i=1; i<m; i++) cnt[i]+=cnt[i-1];
for (int i=n-1; i>=0; i--) sa[ --cnt[ X[ Y[i] ] ] ]=Y[i];
p=1;
swap(X,Y);
X[ sa[0] ]=0;
for (int i=1; i<n; i++) X[ sa[i] ]=Comp(sa[i-1],sa[i],j)? p-1:p++;
}
}
void Calc_height()
{
Height[0]=0;
int k=0;
for (int i=0; i<n; i++)
{
if (k) k--;
if (!X[i]) continue;
int j=sa[ X[i]-1 ];
while (a[i+k]==a[j+k]) k++;
Height[ X[i] ]=k;
}
}
bool Comp1(int x,int y)
{
return X[x]<X[y];
}
int Lcp(int x,int y)
{
x=X[x];
y=X[y];
int z=Lg[y-x];
y-=(1<<z);
return min(ST[x][z],ST[y][z]);
}
int main()
{
//freopen("3879.in","r",stdin);
//freopen("3879.out","w",stdout);
scanf("%d%d",&n,&q);
scanf("%s",&s);
for (int i=0; i<n; i++) a[i]=s[i]-'a'+1;
a[n++]=0;
Da();
Calc_height();
Lg[1]=0;
for (int i=2; i<=n; i++) Lg[i]=Lg[i>>1]+1;
for (int i=0; i<n; i++) ST[i][0]=Height[i+1];
for (int j=1; j<maxl; j++)
for (int i=0; i<n; i++)
{
int mid=min(i+(1<<(j-1)),n-1);
ST[i][j]=min(ST[i][j-1],ST[mid][j-1]);
}
while (q--)
{
scanf("%d",&m);
for (int i=1; i<=m; i++) scanf("%d",&Node[i]),Node[i]--;
sort(Node+1,Node+m+1,Comp1);
tail=1;
sum=lcp[1]=n-Node[1];
num[1]=1;
ans=0;
for (int i=2; i<=m; i++)
if (Node[i-1]!=Node[i])
{
int x=Lcp(Node[i-1],Node[i]);
int Num=0;
while (lcp[tail]>x) sum-=( num[tail]*(lcp[tail]-x) ),Num+=num[tail--];
lcp[++tail]=x;
num[tail]=Num;
ans+=sum;
lcp[++tail]=n-Node[i];
sum+=lcp[tail];
num[tail]=1;
}
ans%=M;
printf("%lld\n",ans);
}
return 0;
}