山海經:線段樹維護最大子段和

liuchanglc發表於2020-08-24

題目描述

“南山之首日鵲山。其首日招搖之山,臨於西海之上,多桂,多金玉。有草焉,其狀如韭而青華,其名日祝餘,食之不飢……又東三百里,日堂庭之山,多棪木,多白猿,多水玉,多黃金。

又東三百八十里,日猨翼之山,其中多怪獸,水多怪魚,多白玉,多蝮蟲,多怪蛇,名怪木,不可以上。……”

《山海經》是以山為綱,以海為線記載古代的河流、植物、動物及礦產等情況,而且每一條記錄路線都不會有重複的山出現。某天,你的地理老師想重遊《山海經》中的路線,為了簡化問題,老師已經把每座山用一個整數表示他對該山的喜惡程度,他想知道第a座山到第b座山的中間某段路(i,j)。能使他感到最滿意,即(i,j)這條路上所有山的喜惡度之和是(c,d)(a≤c≤d≤b)最大值。

於是老師便向你請教,你能幫助他嗎?值得注意的是,在《山海經》中,第i座山只能到達第i+1座山。

輸入格式

輸入第1行是兩個數,n,m,2≤n≤100000,1≤m≤100000n表示一共有n座山,m表示老師想查詢的數目。

2行是n個整數,代表n座山的喜惡度,絕對值均小於10000

以下m行每行有a,b兩個數,1≤a≤j≤b≤m,表示第a座山到第b座山。

輸出格式

一共有m行,每行有3個數i,j,s,表示從第i座山到第j座山總的喜惡度為s。顯然,對於每個查詢,有a≤i≤j≤b,如果有多組解,則輸出i最小的,如果i也相等,則輸出j最小的解。

樣例

樣例輸入

5 3
5 -6 3 -1 4
1 3
1 5
5 5

樣例輸出

1 1 5
3 5 6
5 5 4

分析

題意就是讓你維護區間最大子段和,如果有多個,輸出最靠左的那一個

線段樹要維護區間左端點、右端點、最大子段和、最大字首和、最大字尾和、最大子段和的左右端點、最大字首和的右端點、最大字尾和的左端點、區間和

合併時,區間最大子段和:左區間最大子段和、右區間最大子段和、左區間最大字尾和+右區間最大字首和

區間最大字首和:左區間最大字首和、左區間和+右區間最大字首和

區間最大字尾和:右區間最大字尾和、左區間最大字尾和+右區間和

注意更新最大子段和的端點時要注意最左端的優先

程式碼

#include<cstdio>
const int maxn=1e6+5;
inline int read(){
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0' || ch>'9'){
        if(ch=='-') f=-1;
        ch=getchar();
    }
    while(ch>='0' && ch<='9'){
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    return x*f;
}
struct trr{
    int l,r,mmax,lmax,rmax,jll,jlr,zb,yb,sum;
    trr(){
        mmax=rmax=lmax=-0x3f3f3f3f;
        jll=zb=0x3f3f3f3f;
        jlr=yb=-0x3f3f3f3f;
    }
}tr[maxn];
int a[maxn],n,m;
trr push_up(trr aa,trr bb){
    trr now;
    now.l=aa.l,now.r=bb.r;
    now.sum=aa.sum+bb.sum;
    if(now.mmax<aa.mmax || (now.mmax==aa.mmax && now.jll>aa.jll)){
        now.mmax=aa.mmax;
        now.jll=aa.jll;
        now.jlr=aa.jlr;
    }
    if(now.mmax<bb.mmax || (now.mmax==bb.mmax && now.jll>bb.jll)){
        now.mmax=bb.mmax;
        now.jll=bb.jll;
        now.jlr=bb.jlr;
    }
    if(now.mmax<aa.rmax+bb.lmax || (now.mmax==aa.rmax+bb.lmax && now.jll>aa.yb)){
        now.mmax=aa.rmax+bb.lmax;
        now.jll=aa.yb;
        now.jlr=bb.zb;
    } 
    now.lmax=aa.lmax,now.zb=aa.zb;
    if(aa.sum+bb.lmax>now.lmax){
        now.lmax=aa.sum+bb.lmax;
        now.zb=bb.zb;
    }
    now.rmax=bb.rmax,now.yb=bb.yb;
    if(aa.rmax+bb.sum>=now.rmax){
        now.rmax=aa.rmax+bb.sum;
        now.yb=aa.yb;
    }
    return now;
}
void build(int da,int l,int r){
    tr[da].l=l,tr[da].r=r;
    if(l==r){
        tr[da].mmax=tr[da].lmax=tr[da].rmax=tr[da].sum=a[l];
        tr[da].jll=tr[da].jlr=tr[da].zb=tr[da].yb=l;
        return;
    }
    int mids=(tr[da].l+tr[da].r)>>1;
    build(da<<1,l,mids);
    build(da<<1|1,mids+1,r);
    tr[da]=push_up(tr[da<<1],tr[da<<1|1]);
}
trr cx(int da,int l,int r){
    if(tr[da].l>=l && tr[da].r<=r){
        return tr[da];
    }
    int mids=(tr[da].l+tr[da].r)>>1;
    if(l>mids) return cx(da<<1|1,l,r);
    else if(r<=mids) return cx(da<<1,l,r);
    else return push_up(cx(da<<1,l,r),cx(da<<1|1,l,r));
}
int main(){
    n=read(),m=read();
    for(int i=1;i<=n;i++){
        a[i]=read();
    }
    build(1,1,n);
    for(int i=1;i<=m;i++){
        int l,r;
        l=read(),r=read();
        trr now=cx(1,l,r);
        printf("%d %d %d\n",now.jll,now.jlr,now.mmax);
    }
    return 0;
}

相關文章