[ BZOJ 3879 ] SvT
\(\\\)
Description
给出长度为 \(n\) 的一个字符串, \(m\) 次询问:
每次给出一个 \(t\) ,后面有 \(t\) 个数 \(x_i\) ,求
\[\sum_{i,j\in [1,t],i\not =j} lcp(suf(x_i),suf(x_j))
\]
- \(n\le 5\times 10^5,\sum t\le 3\times 10^6\)
\(\\\)
Solution
设 \(lcp(i,j)\) 表示 \(suf(i),suf(j)\) 的最长公共前缀长度,设 \(LCP(i,j)\) 表示 \(lcp(sa[i],sa[j])\) 。
有一个结论:\(\forall k\in[i,j]\ ,\ LCP(i,j)=\min\{LCP(i,k),LCP(k,j)\}\) 。
证明过程与求任意两后缀 \(lcp\) 相同。
\(\\\)
一个自然的想法是,按照 \(rank\) 将给出的后缀排序。
那么我们只需求出新序列相邻两后缀的 \(lcp\) ,然后剩下的都可以用这些长度取 \(min\) 去刻画了。
求新序列的相邻两后缀 \(lcp\) 可以用 \(height+RMQ\) 搞。
然后询问就变成,一个长度为 \(t-1\) 的序列,所有区间 \(min\) 的和。
做法同 [ AHOI 2013 ] 差异 一题,我和那题一样用的是一种特殊的单调栈,只需扫一遍。
\(\\\)
Code
#include<cmath>
#include<cctype>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 500010
#define M 3000010
#define R register
#define gc getchar
#define mod 23333333333333333ll
using namespace std;
typedef long long ll;
inline ll rd(){
ll x=0; bool f=0; char c=gc();
while(!isdigit(c)){if(c=='-')f=1;c=gc();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
return f?-x:x;
}
ll n,m,s[N],sa[N],t1[N],t2[N],rk[N],h[N],cnt[N];
inline void da(ll n,ll m){
s[n++]=0;
ll *x=t1,*y=t2;
for(R ll i=0;i<n;++i) ++cnt[x[i]=s[i]];
for(R ll i=1;i<m;++i) cnt[i]+=cnt[i-1];
for(R ll i=n-1;~i;--i) sa[--cnt[x[i]]]=i;
for(R ll k=1,p=0;p<n&&k<=n;k<<=1,m=p){
p=0;
for(R ll i=n-k;i<n;++i) y[p++]=i;
for(R ll i=0;i<n;++i) if(sa[i]>=k) y[p++]=sa[i]-k;
for(R ll i=0;i<m;++i) cnt[i]=0;
for(R ll i=0;i<n;++i) ++cnt[x[y[i]]];
for(R ll i=1;i<m;++i) cnt[i]+=cnt[i-1];
for(R ll i=n-1;~i;--i) sa[--cnt[x[y[i]]]]=y[i];
swap(x,y); p=1; x[sa[0]]=0;
for(R ll i=1;i<n;++i)
x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i-1]+k]==y[sa[i]+k])?p-1:p++;
}
--n; h[0]=0;
for(R ll i=0;i<n;++i) sa[i]=sa[i+1];
for(R ll i=0;i<n;++i) rk[sa[i]]=i;
for(R ll i=0,p=0;i<n;++i){
if(!rk[i]) continue;
if(p) --p;
while(s[i+p]==s[sa[rk[i]-1]+p]) ++p;
h[rk[i]]=p;
}
}
struct ST{
ll c[N][20];
inline void reset(){
for(R ll i=0;i<n;++i) c[i][0]=h[i];
for(R ll k=1;(1ll<<k)<=n;++k)
for(R ll i=0;i<n-k;++i) c[i][k]=min(c[i][k-1],c[i+(1ll<<(k-1))][k-1]);
}
inline ll query(ll l,ll r){
ll t=log2(r-l+1);
return min(c[l][t],c[r-(1ll<<t)+1][t]);
}
}st;
ll c[M],sum,top;
struct S{int x,cnt;}stk[M];
inline bool cmp(ll x,ll y){return rk[x]<rk[y];}
inline void work(){
ll t=rd(),res=0;
for(R int i=1;i<=t;++i) c[i]=rd()-1;
sort(c+1,c+1+t);
for(R int i=1,tmp=0;i<=t;++i){
c[++tmp]=c[i];
while(c[i+1]==c[i]&&i<=t) ++i;
if(i==t){t=tmp;break;}
}
if(t<=1){puts("0");return;}
sort(c+1,c+1+t,cmp);
sum=top=0;
for(R ll i=1,now,cnt=1;i<t;++i,cnt=1){
now=st.query(rk[c[i]]+1,rk[c[i+1]]);
while(top&&stk[top].x>=now){
cnt+=stk[top].cnt;
sum-=stk[top].cnt*stk[top].x;
--top;
}
stk[++top].x=now;
stk[top].cnt=cnt;
(sum+=stk[top].x*stk[top].cnt)%=mod;
(res+=sum)%=mod;
}
printf("%lld\n",res);
}
int main(){
n=rd(); m=rd();
char c=gc(); while(!isalpha(c)) c=gc();
s[0]=c-' ';
for(R ll i=2;i<=n;++i) s[i-1]=gc()-' ';
da(n,256); st.reset();
while(m--) work();
return 0;
}