义乌中学暑假集训 2021.7.8 D

2022-09-19 13:29:50 浏览数 (1)

义乌中学暑假集训 2021.7.8 D

Preface

见到 lxl 小姐姐真的好好好兴奋!!!

果然好可爱

Description

给定一个序列 A,求所有 1leq l leq r leq n 的区间 [l,r] 的最大子段和的和,答案对 2^{64} 取模。

1leq nleq 10^5,-10^9leq A_ileq 10^9

Solution

考虑分治,求跨越左右两个区间的贡献。

维护一下区间的 suf 表示后缀和,pre 表示前缀和,sl 表示左区间的后缀最大子段和,sr 表示右区间的前缀最大子段和。

lleq i leq mid,mid 1leq jleq r,显然答案为 max{suf_i pre_j,sl_i,sr_j,}

然而三个数的最大值难以维护,所以考虑拆成两种情况。

  1. sl_ige sr_j 所以答案就变成了 max{suf_i pre_j,sl_i},然后先减去 suf_i,再加回去,可以得到 max{pre_j,sl_i-suf_i} suf_i,这样 j 就独自只在一个候选 max 之中了,这样比较好维护。 那么再继续拆分,分两类讨论,直接根据以上条件二维偏序即可
  2. sl_i<sr_j1 同理,这里不再赘述。

然后二维偏序可以直接用 sort BIT 进行,博主是开了 3 个树状数组。

最后喷一下 lxl,明明 pre,suf,sl,sr 全是有单调性的,直接二分就好了(害我写了二维偏序跑得比二分慢了 8s

放张图纪念一下我的推狮子:

Code

代码语言:javascript复制
#include<bits/stdc  .h>
#define I inline
#define W while
#define RI register int 
#define Cn const
#define CI Cn int&
#define gc getchar
#define pc putchar 
#define int LL
#define LL long long 
#define ull unsigned long long 
using namespace std;
Cn int N=1e5 10;
I void read(int& x){RI f=1;char c=gc();x=0;W(!('0'<=c&&c<='9')) f=c^'-'?f:-1,c=gc();W('0'<=c&&c<='9') x=x*10 (c-'0'),c=gc();x*=f;}
I void write(ull x){x>=10&&(write(x/10),0),pc(x '0');}
int n,a[N];
LL P,s1[N],s2[N],pre[N],suf[N];
ull Ans;
struct node{LL pre,suf,sum,ans;}T[N<<2];
I node operator   (Cn node& x,Cn node& y){return (node){max(x.pre,x.sum y.pre),max(x.suf y.sum,y.suf),x.sum y.sum,max(max(x.ans,y.ans),x.suf y.pre)};}
class SegmentTree{//线段树求最大子段和
    private:
        #define mid (l r>>1)
        #define PT CI x=1,CI l=1,CI r=n
        #define LT x<<1,l,mid
        #define RT x<<11,mid 1,r
        #define PU(x) (T[x]=T[x<<1] T[x<<11])
    public:
        I void B(PT){
            if(l==r) return void(T[x]=(node){a[l],a[l],a[l],a[l]});
            B(LT),B(RT),PU(x);
        }
        I node Q(CI L,CI R,PT){
            if(L<=l&&r<=R) return T[x];
            if(R<=mid) return Q(L,R,LT);if(L>mid) return Q(L,R,RT);
            return Q(L,R,LT) Q(L,R,RT);
        }
}S;
struct Temp{LL s;int id;};
I bool cmp1(Cn Temp& x,Cn Temp& y){return x.s<y.s;}
I bool cmp2(Cn Temp& x,Cn Temp& y){return x.s>y.s;}
class TreeArray{//树状数组
    private:
        LL c[N];
        #define lowbit(x) (x&(-x))
    public:
        I void C(CI x){RI i;for(i=x;i<=n;i =lowbit(i)) c[i]=0;}
        I void U(CI x,CI y){RI i;for(i=x;i<=n;i =lowbit(i)) c[i] =y;}
        I LL Q(CI x){RI i;LL S=0;for(i=x;i;i-=lowbit(i)) S =c[i];return S;}
}A,B,C;
#define LW(x) (lower_bound(b 1,b c2 1,x)-b)
I void Solve(CI l,CI r){
    if(l==r) return void(Ans =a[l]);
    RI i,j,c1,c2;Solve(l,mid),Solve(mid 1,r);Temp t[r-l 5];LL b[r-l 5];
    for(suf[mid]=a[mid],i=mid-1;i>=l;i--) suf[i]=suf[i 1] a[i];
    for(pre[mid 1]=a[mid 1],i=mid 2;i<=r;i  ) pre[i]=pre[i-1] a[i];//预处理出前/后缀和
    for(s1[mid]=S.Q(mid,mid).ans,i=mid-1;i>=l;i--) s1[i]=max(s1[i 1],S.Q(i,mid).ans);
    for(s2[mid 1]=S.Q(mid 1,mid 1).ans,i=mid 2;i<=r;i  ) s2[i]=max(s2[i-1],S.Q(mid 1,i).ans);//预处理出前/后缀最大子段和
    for(i=mid-1;i>=l;i--) suf[i]=max(suf[i 1],suf[i]);for(i=mid 2;i<=r;i  ) pre[i]=max(pre[i],pre[i-1]);//记得取 max
    for(c2=0,i=mid 1;i<=r;i  ) b[  c2]=pre[i];for(i=l;i<=mid;i  ) b[  c2]=s1[i]-suf[i];//第一种情况
    sort(b 1,b c2 1),c2=unique(b 1,b c2 1)-b-1;
    for(c1=0,i=mid 1;i<=r;i  ) t[  c1]=(Temp){s2[i],i};
    for(sort(t 1,t c1 1,cmp1),j=1,i=mid;i>=l;i--){
        W(j<=c1&&t[j].s<=s1[i]) A.U(LW(pre[t[j].id]),1),B.U(LW(pre[t[j].id]),pre[t[j].id]),j  ;//二维偏序
        Ans =A.Q(LW(s1[i]-suf[i]))*s1[i] (A.Q(n)-A.Q(LW(s1[i]-suf[i])))*suf[i] (B.Q(n)-B.Q(LW(s1[i]-suf[i])));
    }for(i=1;i<j;i  ) A.C(LW(pre[t[i].id])),B.C(LW(pre[t[i].id]));//记得清空
    for(c2=0,i=mid 1;i<=r;i  ) b[  c2]=s2[i]-pre[i];for(i=l;i<=mid;i  ) b[  c2]=suf[i];//第二种情况
    sort(b 1,b c2 1),c2=unique(b 1,b c2 1)-b-1;
    for(sort(t 1,t c1 1,cmp2),j=1,i=l;i<=mid;i  ){
        W(j<=c1&&t[j].s>s1[i]) A.U(LW(s2[t[j].id]-pre[t[j].id]),1),B.U(LW(s2[t[j].id]-pre[t[j].id]),pre[t[j].id]),C.U(LW(s2[t[j].id]-pre[t[j].id]),s2[t[j].id]),j  ;//二维偏序
        Ans =A.Q(LW(suf[i]))*suf[i] B.Q(LW(suf[i])) (C.Q(n)-C.Q(LW(suf[i])));
    }for(i=1;i<j;i  ) A.C(LW(s2[t[i].id]-pre[t[i].id])),B.C(LW(s2[t[i].id]-pre[t[i].id])),C.C(LW(s2[t[i].id]-pre[t[i].id]));//记得清空
}
signed main(){
    RI i;for(read(n),i=1;i<=n;i  ) read(a[i]);
    return S.B(),Solve(1,n),write(Ans),pc('n'),0;//建线段树(求区间最大子段和)、分治
}

0 人点赞