[ 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;
}

posted @ 2018-11-20 17:22  SGCollin  阅读(197)  评论(0编辑  收藏  举报