zoukankan      html  css  js  c++  java
  • 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$去重的时候不能

    必须

    Code

      1 #include<iostream>
      2 #include<cstring>
      3 #include<cstdio>
      4 #include<vector>
      5 #include<algorithm>
      6 #define N (500009)
      7 #define LL long long
      8 #define MOD (23333333333333333LL)
      9 using namespace std;
     10 
     11 int n,m=120,q,t,L[N],R[N],stack[N],top;
     12 int wt[N],wa[N],wb[N];
     13 int SA[N],Rank[N],Height[N];
     14 int LOG2[N],ST[N][21];
     15 char r[N];
     16 vector<int>v,w;
     17 
     18 inline int read()
     19 {
     20     int x=0,w=1; char c=getchar();
     21     while (c<'0' || c>'9') {if (c=='-') w=-1; c=getchar();}
     22     while (c>='0' && c<='9') x=x*10+c-'0', c=getchar();
     23     return x*w;
     24 }
     25 
     26 bool cmp(int *y,int a,int b,int k)
     27 {
     28     int arank1=y[a];
     29     int brank1=y[b];
     30     int arank2=a+k>=n?-1:y[a+k];
     31     int brank2=b+k>=n?-1:y[b+k];
     32     return arank1==brank1 && arank2==brank2;
     33 }
     34 
     35 void Build_SA()
     36 {
     37     int *x=wa,*y=wb;
     38     for (int i=0; i<m; ++i) wt[i]=0;
     39     for (int i=0; i<n; ++i) wt[x[i]=r[i]]++;
     40     for (int i=1; i<m; ++i) wt[i]+=wt[i-1];
     41     for (int i=n-1; i>=0; --i) SA[--wt[x[i]]]=i;
     42     
     43     for (int j=1; j<=n; j<<=1)
     44     {
     45         int p=0;
     46         for (int i=n-j; i<n; ++i) y[p++]=i;
     47         for (int i=0; i<n; ++i) if (SA[i]>=j) y[p++]=SA[i]-j;
     48         
     49         for (int i=0; i<m; ++i) wt[i]=0;
     50         for (int i=0; i<n; ++i) wt[x[y[i]]]++;
     51         for (int i=1; i<m; ++i) wt[i]+=wt[i-1];
     52         for (int i=n-1; i>=0; --i) SA[--wt[x[y[i]]]]=y[i];
     53         
     54         m=1; swap(x,y);
     55         x[SA[0]]=0;
     56         for (int i=1; i<n; ++i)
     57             x[SA[i]]=cmp(y,SA[i],SA[i-1],j)?m-1:m++;
     58         if (m>=n) break;
     59     }
     60 }
     61 
     62 void Build_Height()
     63 {
     64     for (int i=0; i<n; ++i) Rank[SA[i]]=i;
     65     int k=0; Height[0]=0;
     66     for (int i=0; i<n; ++i)
     67     {
     68         if (!Rank[i]) continue;
     69         if (k) k--;
     70         int j=SA[Rank[i]-1];
     71         while (r[i+k]==r[j+k]) k++;
     72         Height[Rank[i]]=k;
     73     }
     74 }
     75 
     76 void Build_ST()
     77 {
     78     int t=0;
     79     for(int i=2;i<=n;i++) LOG2[i]=LOG2[i>>1]+1;
     80     for (int i=0; i<n; ++i) ST[i][0]=Height[i];
     81     for (int j=1; j<=20; ++j)
     82         for (int i=0; i+(1<<j)-1<n; ++i)    
     83             ST[i][j]=min(ST[i][j-1],ST[i+(1<<j-1)][j-1]);
     84 }
     85 
     86 int Query(int l,int r)
     87 {
     88     int k=LOG2[r-l+1];
     89     return min(ST[l][k],ST[r-(1<<k)+1][k]);
     90 }
     91 
     92 bool cmp1(int a,int b)
     93 {
     94     return Rank[a]<Rank[b];
     95 }
     96 
     97 int main()
     98 {
     99     n=read(); q=read(); scanf("%s",r);
    100     Build_SA(); Build_Height(); Build_ST();
    101     while (q--)
    102     {
    103         v.clear(); w.clear();
    104         t=read();
    105         for (int i=1; i<=t; ++i) v.push_back(read()-1);
    106         sort(v.begin(),v.end()); 
    107         v.erase(unique(v.begin(),v.end()), v.end());
    108         sort(v.begin(),v.end(),cmp1);
    109         w.push_back(-1);
    110         for (int i=1; i<v.size(); ++i)
    111             w.push_back(Query(Rank[v[i-1]]+1,Rank[v[i]]));
    112         w.push_back(-1);
    113         int ws=w.size();
    114         stack[top=1]=0;
    115         for (int i=1; i<ws-1; ++i)
    116         {
    117             while (top && w[stack[top]]>w[i]) top--;
    118             L[i]=stack[top]; stack[++top]=i;
    119         }
    120         stack[top=1]=ws-1;
    121         for (int i=ws-2; i>=1; --i)
    122         {
    123             while (top && w[stack[top]]>=w[i]) top--;
    124             R[i]=stack[top]; stack[++top]=i;
    125         }
    126         LL ans=0;
    127         for (int i=1; i<ws-1; ++i) (ans+=1ll*(i-L[i])*(R[i]-i)%MOD*w[i]%MOD)%MOD;
    128         printf("%lld
    ",ans);
    129     }
    130 }
  • 相关阅读:
    UVA 11991 Easy Problem from Rujia Liu(map,vector的使用)
    UVA 11995 I Can Guess the Data Structure! (STL应用)
    HDU 2795 Billboard(线段树,单点更新)
    HDU 1394 Minimum Inversion Number (线段树,单点更新)
    UVA 11827 Maximum GCD(读入技巧,stringstream的使用)
    contest 2 总结
    Const 1 总结
    开始进行大量题目练习
    函数式线段树的个人理解
    poj 2318 TOYS
  • 原文地址:https://www.cnblogs.com/refun/p/10392264.html
Copyright © 2011-2022 走看看