zoukankan      html  css  js  c++  java
  • [ BZOJ 3879 ] SvT

    (\)

    Description


    给出长度为 (n) 的一个字符串, (m) 次询问:

    每次给出一个 (t) ,后面有 (t) 个数 (x_i) ,求

    [sum_{i,jin [1,t],i ot =j} lcp(suf(x_i),suf(x_j)) ]

    • (nle 5 imes 10^5,sum tle 3 imes 10^6)

    (\)

    Solution


    (lcp(i,j)) 表示 (suf(i),suf(j)) 的最长公共前缀长度,设 (LCP(i,j)) 表示 (lcp(sa[i],sa[j]))

    有一个结论:(forall kin[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
    ",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;
    }
    
    
  • 相关阅读:
    mysql存储过程
    命令简写 ~/.bash_aliases
    TestCafe 快速上手 (三)
    TestCafe 快速上手 (二)
    OWASP 文档
    读书笔记
    读书笔记
    类数组转化为真正的数组
    Vue子组件向父组件传递数据
    node.js取参四种方法req.body,req.params,req.param,req.body
  • 原文地址:https://www.cnblogs.com/SGCollin/p/9990170.html
Copyright © 2011-2022 走看看