• Gym


    题意:求一个序列中本质不同的连续子序列的最大值之和。

    由于要求“本质不同”,所以后缀数组就派上用场了,可以从小到大枚举每个后缀,对于每个sa[i],从sa[i]+ht[i]开始枚举(ht[0]=0),这样就能不重复不遗漏地枚举出每一个子串。

    但是这样做,最坏情况仍旧是$O(n^2)$的,可能会被卡掉,需要进一步优化。

    对于每个sa[i],设k=sa[i]+ht[i],则问题转化成了求max(s[sa[i]],s[sa[i]+1],...,s[k])+max(s[sa[i]],s[sa[i]+1],...,s[k+1])+...+max(s[sa[i]],s[sa[i]+1],...,s[n-1])。

    可以发现,随着下标的增大,最大值是单调不减的,这启示我们利用单调栈将后缀进行分段,对于每个最大值不同的段求出后缀和,对于每个sa[i],利用RMQ求出s[sa[i],k]中的最大值mx,然后在单调栈上二分找到第一个大于mx的值的下标,将贡献分成左右两部分相加即可。

    一开始可以将初始序列离散化,求出后缀数组后再还原回去,这样复杂度就不依赖于序列元素的大小而只与序列长度有关了,$O(nlogn)$

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 typedef long long ll;
     4 const int N=2e5+10,inf=0x3f3f3f3f;
     5 int s[N],buf1[N],buf2[N],b[N],nb,c[N],n,sa[N],ht[N],rnk[N],ST[N][20],Log[N],sta[N],idx[N],tp;
     6 ll sum[N];
     7 void Sort(int* x,int* y,int m) {
     8     for(int i=0; i<m; ++i)c[i]=0;
     9     for(int i=0; i<n; ++i)++c[x[i]];
    10     for(int i=1; i<m; ++i)c[i]+=c[i-1];
    11     for(int i=n-1; i>=0; --i)sa[--c[x[y[i]]]]=y[i];
    12 }
    13 void da(int* s,int n,int m=1000) {
    14     int *x=buf1,*y=buf2;
    15     x[n]=y[n]=-1;
    16     for(int i=0; i<n; ++i)x[i]=s[i],y[i]=i;
    17     Sort(x,y,m);
    18     for(int k=1; k<n; k<<=1) {
    19         int p=0;
    20         for(int i=n-k; i<n; ++i)y[p++]=i;
    21         for(int i=0; i<n; ++i)if(sa[i]>=k)y[p++]=sa[i]-k;
    22         Sort(x,y,m),p=1,y[sa[0]]=0;
    23         for(int i=1; i<n; ++i)y[sa[i]]=x[sa[i-1]]==x[sa[i]]&&x[sa[i-1]+k]==x[sa[i]+k]?p-1:p++;
    24         if(p==n)break;
    25         swap(x,y),m=p;
    26     }
    27 }
    28 void getht() {
    29     for(int i=0; i<n; ++i)rnk[sa[i]]=i;
    30     ht[0]=0,s[n]=-1;
    31     for(int i=0,k=0; i<n; ++i) {
    32         if(k)--k;
    33         if(!rnk[i])continue;
    34         for(; s[i+k]==s[sa[rnk[i]-1]+k]; ++k);
    35         ht[rnk[i]]=k;
    36     }
    37 }
    38 void build() {
    39     for(int i=0; i<n; ++i)ST[i][0]=s[i];
    40     for(int k=1; k<=Log[n]; ++k)
    41         for(int i=0; i+(1<<k)-1<n; ++i)
    42             ST[i][k]=max(ST[i][k-1],ST[i+(1<<(k-1))][k-1]);
    43 }
    44 int qry(int L,int R) {
    45     int k=Log[R-L+1];
    46     return max(ST[L][k],ST[R-(1<<k)+1][k]);
    47 }
    48 struct QR {
    49     int k,mx;
    50     bool operator<(const QR& b)const {return k<b.k;}
    51 } qr[N];
    52 void push(int x,int i) {
    53     for(; ~tp&&x>=sta[tp]; --tp);
    54     sta[++tp]=x,sum[tp]=(ll)x*(idx[tp-1]-i)+sum[tp-1],idx[tp]=i;
    55 }
    56 int main() {
    57     Log[0]=-1;
    58     for(int i=1; i<N; ++i)Log[i]=Log[i>>1]+1;
    59     int T;
    60     for(scanf("%d",&T); T--;) {
    61         scanf("%d",&n);
    62         for(int i=0; i<n; ++i)scanf("%d",&s[i]);
    63         nb=0;
    64         for(int i=0; i<n; ++i)b[nb++]=s[i];
    65         sort(b,b+nb),nb=unique(b,b+nb)-b;
    66         for(int i=0; i<n; ++i)s[i]=lower_bound(b,b+nb,s[i])-b;
    67         da(s,n,n+10),getht();
    68         for(int i=0; i<n; ++i)s[i]=b[s[i]];
    69         build();
    70         ll ans=0;
    71         for(int i=0; i<n; ++i) {
    72             int k=sa[i]+ht[i];
    73             qr[i]= {k,qry(sa[i],k)};
    74         }
    75         sort(qr,qr+n);
    76         sta[tp=0]=inf,sum[tp]=0,idx[tp]=n;
    77         for(int i=n-1,j=n-1; i>=0; --i) {
    78             int k=qr[i].k,mx=qr[i].mx;
    79             for(; j>=k; --j)push(s[j],j);
    80             int t=lower_bound(sta,sta+tp+1,mx,greater<int>())-sta-1;
    81             ans+=(ll)mx*(idx[t]-k)+sum[t];
    82         }
    83         printf("%lld
    ",ans);
    84     }
    85     return 0;
    86 }
  • 相关阅读:
    约数个数 和 约数之和
    二分模板
    新生赛补题
    codefores刷题心得3 思维+dp(特别好玩)
    二叉树的遍历及例题
    团队作业七——团队作业分配
    WarPlane——游戏设计文档
    团队作业(五)
    团队作业(四)
    团队项目方案分析
  • 原文地址:https://www.cnblogs.com/asdfsag/p/11681118.html
Copyright © 2020-2023  润新知