BZOJ3879:SvT(后缀数组,单调栈,ST表)

Description

(我并不想告诉你题目名字是什么鬼)

有一个长度为n的仅包含小写字母的字符串S,下标范围为[1,n].

现在有若干组询问,对于每一个询问,我们给出若干个后缀(以其在S中出现的起始位置来表示),求这些后缀两两之间的LCP(LongestCommonPrefix)的长度之和.一对后缀之间的LCP长度仅统计一遍.

Input

第一行两个正整数n,m,分别表示S的长度以及询问的次数.

接下来一行有一个字符串S.

接下来有m组询问,对于每一组询问,均按照以下格式在一行内给出:

首先是一个整数t,表示共有多少个后缀.接下来t个整数分别表示t个后缀在字符串S中的出现位置.

Output

对于每一组询问,输出一行一个整数,表示该组询问的答案.由于答案可能很大,仅需要输出这个答案对于23333333333333333(一个巨大的质数)取模的余数.

Sample Input

7 3
popoqqq
1 4
2 3 5
4 1 2 5 6

Sample Output

0
0
2
Hint
样例解释:
对于询问一,只有一个后缀”oqqq”,因此答案为0.
对于询问二,有两个后缀”poqqq”以及”qqq”,两个后缀之间的LCP为0,因此答案为0.
对于询问三,有四个后缀”popoqqq”,”opoqqq”,”qqq”,”qq”,其中只有”qqq”,”qq”两个后缀之间的LCP不为0,且长度为2,因此答案为2.
对于100%的测试数据,有S<=5*10^5,且Σt<=3*10^6.
特别注意:由于另一世界线的某些参数发生了变化,对于一组询问,即使一个后缀出现了多次,也仅算一次.

Solution

首先把所有后缀按照$Rank$排序,然后用$ST$表求出相邻的$lcp$。

现在问题变成了对于一个序列的所有区间$(x,y)$,求出每个区间内的最小值的和。

逆向思维一下,考虑每个值可以作为最小值在哪些区间内出现,

也就是对每一个数求左边第一个小于等于它的数和右边第一个小于它的数,这个可以用单调栈维护。

$vector$去重的时候不能

BZOJ3879:SvT(后缀数组,单调栈,ST表)

必须

BZOJ3879:SvT(后缀数组,单调栈,ST表)

Code

 #include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<algorithm>
#define N (500009)
#define LL long long
#define MOD (23333333333333333LL)
using namespace std; int n,m=,q,t,L[N],R[N],stack[N],top;
int wt[N],wa[N],wb[N];
int SA[N],Rank[N],Height[N];
int LOG2[N],ST[N][];
char r[N];
vector<int>v,w; inline int read()
{
int x=,w=; char c=getchar();
while (c<'' || c>'') {if (c=='-') w=-; c=getchar();}
while (c>='' && c<='') x=x*+c-'', c=getchar();
return x*w;
} bool cmp(int *y,int a,int b,int k)
{
int arank1=y[a];
int brank1=y[b];
int arank2=a+k>=n?-:y[a+k];
int brank2=b+k>=n?-:y[b+k];
return arank1==brank1 && arank2==brank2;
} void Build_SA()
{
int *x=wa,*y=wb;
for (int i=; i<m; ++i) wt[i]=;
for (int i=; i<n; ++i) wt[x[i]=r[i]]++;
for (int i=; i<m; ++i) wt[i]+=wt[i-];
for (int i=n-; i>=; --i) SA[--wt[x[i]]]=i; for (int j=; j<=n; j<<=)
{
int p=;
for (int i=n-j; i<n; ++i) y[p++]=i;
for (int i=; i<n; ++i) if (SA[i]>=j) y[p++]=SA[i]-j; for (int i=; i<m; ++i) wt[i]=;
for (int i=; i<n; ++i) wt[x[y[i]]]++;
for (int i=; i<m; ++i) wt[i]+=wt[i-];
for (int i=n-; i>=; --i) SA[--wt[x[y[i]]]]=y[i]; m=; swap(x,y);
x[SA[]]=;
for (int i=; i<n; ++i)
x[SA[i]]=cmp(y,SA[i],SA[i-],j)?m-:m++;
if (m>=n) break;
}
} void Build_Height()
{
for (int i=; i<n; ++i) Rank[SA[i]]=i;
int k=; Height[]=;
for (int i=; i<n; ++i)
{
if (!Rank[i]) continue;
if (k) k--;
int j=SA[Rank[i]-];
while (r[i+k]==r[j+k]) k++;
Height[Rank[i]]=k;
}
} void Build_ST()
{
int t=;
for(int i=;i<=n;i++) LOG2[i]=LOG2[i>>]+;
for (int i=; i<n; ++i) ST[i][]=Height[i];
for (int j=; j<=; ++j)
for (int i=; i+(<<j)-<n; ++i)
ST[i][j]=min(ST[i][j-],ST[i+(<<j-)][j-]);
} int Query(int l,int r)
{
int k=LOG2[r-l+];
return min(ST[l][k],ST[r-(<<k)+][k]);
} bool cmp1(int a,int b)
{
return Rank[a]<Rank[b];
} int main()
{
n=read(); q=read(); scanf("%s",r);
Build_SA(); Build_Height(); Build_ST();
while (q--)
{
v.clear(); w.clear();
t=read();
for (int i=; i<=t; ++i) v.push_back(read()-);
sort(v.begin(),v.end());
v.erase(unique(v.begin(),v.end()), v.end());
sort(v.begin(),v.end(),cmp1);
w.push_back(-);
for (int i=; i<v.size(); ++i)
w.push_back(Query(Rank[v[i-]]+,Rank[v[i]]));
w.push_back(-);
int ws=w.size();
stack[top=]=;
for (int i=; i<ws-; ++i)
{
while (top && w[stack[top]]>w[i]) top--;
L[i]=stack[top]; stack[++top]=i;
}
stack[top=]=ws-;
for (int i=ws-; i>=; --i)
{
while (top && w[stack[top]]>=w[i]) top--;
R[i]=stack[top]; stack[++top]=i;
}
LL ans=;
for (int i=; i<ws-; ++i) (ans+=1ll*(i-L[i])*(R[i]-i)%MOD*w[i]%MOD)%MOD;
printf("%lld\n",ans);
}
}
上一篇:python面向对象编程进阶


下一篇:dns解析对SEO产生的影响