1. 程式人生 > >(CDOJ 844 線段樹區間最大連續和 )

(CDOJ 844 線段樹區間最大連續和 )

Problem

給定一個序列,每個點有相應權值
有兩種操作:
①修改某個點的權值
②查詢給定區間的最大連續區間和

Solution

  • 首先要理解清楚題意,是在給定區間內求最大連續子序列。所以暴力的方法很簡單,每次在給定區間上貪心地求即可。複雜度O(mn)
  • 優化一點可以用線段樹
  • 線段樹上維護4個值:區間總和s,從左端起的最大連續區間和ls,從右端起的最大連續區間和rs,整個區間最終的最大連續區間和ms。
  • pushup操作

ls的修改

可以更新為左子樹的ls,或者左子樹的s+右子樹的ls,分別對應第一條紅線和第二條紅線

rs的修改
同ls的更新一樣,可以更新為右子樹的rs,或者右子樹的s+左子樹的ls
目的是保證從端點開始

ms的修改
正常的情況是更新為左右子樹的max(ms),但是有一種特殊情況:
左子樹的rs和右子樹的ls是連續的,因此有可能左子樹的rs+右子樹的ls的值更大
所以一共三種情況

s的修改
很簡單,把左右子樹的s加起來即可
一般來說求最值不需要記錄區間和,但這個問題涉及到區間的交併,s可以用於解決完全包含某個子區間的問題(如ls,rs的修改時)

  • query是這個題最難的部分
    ⅰ 同樣要維護4個全域性變數:區間最大連續子序列anss(即最終答案),從左端點開始的最大連續子序列ansl,從右端點開始的最大連續子序列ansr,已經更新過的區間和sum(同樣需要區間和來幫助詢問答案)
    ⅱ 線段樹一個很重要的性質是:每次更新或者詢問一定是先處理左子樹,後處理右子樹,因此sum維護的是從詢問區間最左端到當前區間的區間和,利用sum可以很巧妙的維護其它3個的值
    ⅲ 具體實現的方法
    if(ll<=L&&rr>=R){
        ansl=max(ansl,sum+t[pos].ls);//不更新,或者更新為:左面所有的值的和sum+當前區間的ls,利用線段樹更新的順序是先左後右,可以確保ansl維護的值是從最左端開始的
        anss=max(ansl,anss);
        anss=max(anss,t[pos].ms);
        anss=max(anss,ansr+t[pos].ls);
        ansr=max(ansr+t[pos].s,t[pos].rs);//同樣利用sum的性質更新
        anss=max
(ansr,anss); sum+=t[pos].s; return; }

ⅳ 最後需要注意的是,ansr的更新必須放在anss之後,因為如果先更新ansr,那麼ansr的值有可能會包含當前區間的值,這樣會造成重複

Code

// by spli
#include<cstring>
#include<cstdio>
#include<iostream>
#include<algorithm>
#define LL long long
using namespace std;

const int N=500010;
const LL inf=100000000ll;
int n,m;
struct seg{
    int L,R;
    LL s,ms,ls,rs;
}t[N*4+5];
LL ansl=-inf,ansr=-inf,anss=-inf,sum=0;

void pushup(int pos){
    t[pos].s=t[pos<<1].s+t[pos<<1|1].s;
    t[pos].ms=max(t[pos<<1].ms,max(t[pos<<1|1].ms,t[pos<<1].rs+t[pos<<1|1].ls));
    t[pos].ls=max(t[pos<<1].ls,t[pos<<1].s+t[pos<<1|1].ls);
    t[pos].rs=max(t[pos<<1|1].rs,t[pos<<1|1].s+t[pos<<1].rs);
}

void build(int pos,int L,int R){
    t[pos].L=L;
    t[pos].R=R;
    if(L==R){
        scanf("%lld",&t[pos].s);
        t[pos].ms=t[pos].ls=t[pos].rs=t[pos].s;
        return;
    }
    int mid=(L+R)>>1;
    build(pos<<1,L,mid);
    build(pos<<1|1,mid+1,R);
    pushup(pos);
}

void update(int pos,int k,int v){
    int L=t[pos].L;
    int R=t[pos].R;
    if(L==R){
        t[pos]=(seg){L,R,v,v,v,v};
        return;
    }
    int mid=(L+R)>>1;
    if(k<=mid) update(pos<<1,k,v);
    else update(pos<<1|1,k,v);
    pushup(pos);
}

void query(int pos,int ll,int rr){
    int L=t[pos].L;
    int R=t[pos].R;
    if(ll>R||L>rr) return;
    if(ll<=L&&rr>=R){
        ansl=max(ansl,sum+t[pos].ls);
        anss=max(ansl,anss);
        anss=max(anss,t[pos].ms);
        anss=max(anss,ansr+t[pos].ls);
        ansr=max(ansr+t[pos].s,t[pos].rs);
        anss=max(ansr,anss);
        sum+=t[pos].s;
        return;
    }
    int mid=(L+R)>>1;
    query(pos<<1,ll,rr);
    query(pos<<1|1,ll,rr);
}

int main(){
    scanf("%d%d",&n,&m);
    build(1,1,n);
    int k,x,y;
    for(int i=1;i<=m;++i){
        scanf("%d%d%d",&k,&x,&y);
        if(k==1){
            anss=ansl=ansr=-inf;sum=0;
            query(1,x,y);
            printf("%lld\n",anss);
        }
        if(k==2) update(1,x,y);
    }
    return 0;
}