【SPOJ GSS3】Can you answer these queries III(動態區間最大子段和)——楊子曰題目

【SPOJ GSS3】Can you answer these queries III(動態區間最大段和)——楊子曰題目

傳送門:線段樹集合


題目描述
You are given a sequence A of N (N <= 50000) integers between -10000 and 10000. On this sequence you have to apply M (M <= 50000) operations:
modify the i-th element in the sequence or for given x y print max{Ai + Ai+1 + … + Aj | x<=i<=j<=y }.

輸入格式:
The first line of input contains an integer N. The following line contains N integers, representing the sequence A1…AN.
The third line contains an integer M. The next M lines contain the operations in following form:
0 x y: modify Ax into y (|y|<=10000).
1 x y: print max{Ai + Ai+1 + … + Aj | x<=i<=j<=y }.

輸出格式:
For each query, print an integer as the problem required.

輸入樣例:

4
1 2 3 4
4
1 1 3
0 3 -3
1 2 4
1 3 3

輸出樣例:

6
4
-3

給一個長度爲n(n<=50000)的序列,再給出m個接着是m(m<=20000)個操作對於每個操作k,x,y,如果k=0那麼第x個元素變爲y,如果k=1就求出區間[x,y]裏的子序列最大和


額……線段樹我又來了


這次我們要維護的東西就有點多了,首先一個正常的人都能知道區間裏的區間子序列最大和——ms肯定要維護(這不就是答案嗎),現在我們要考慮想要算出ms,也就是pushup的時候要用到什麼東西
我們來分類討論一下:
這個區間的子序列最大和都在左區間:
在這裏插入圖片描述
嗯……不錯,直接用ms[nod*2]更新上來

這個區間的子序列最大和都在右區間:
在這裏插入圖片描述
也直接用ms[nod*2+1]更新上來

但是最令人惱火的就是一半在左,一半在右,Look at the 圖:
在這裏插入圖片描述
我們很明顯地知道,藍色和綠色部分很可能不是左右區間的子序列最大和,那到底是神馬呢?
我覺得藍色部分是左區間包括右端點的區間最大子序列和,同理綠色部分是右區間包括左端點的區間最大子序列和(←仔細閱讀)
也就是我們還要維護每個區間從左邊和右邊開始的區間子序列最大和ls和rs
那我們就可以得到:

ms[nod]=max(ms[nos*2],ms[nod*2+1],rs[nod*2]+ls[nod*2+1]);

不要高興太早,我們還要考慮ls和rs怎麼pushup,我們以ls爲例分類討論一下:
這個區間的ls就是左區間的ls
在這裏插入圖片描述
二話不說,直接用ls[nod*2]更新上來

還有一種噁心的情況,叫做橫跨了左右區間,look at the圖:
在這裏插入圖片描述
我們會發現藍色部分是左區間的和,而綠色部分是右區間的ls
這也就是說——我!們!還!要!維!護!一!個!sum
於是我們得出了這樣的東西:

ls[nod]=max(ls[nod*2],sum[nod*2]+ls[nod*2+1]);
rs[nod]=max(rs[nod*2+1],sum[nod*2+1]+rs[nod*2]);

總結一波:到目前爲止我們已經把我們要維護的東西維護完了——ms,ls,rs,sum
於是乎,我們得到了以下pushup:

void pushup(int nod){
	ms[nod]=max(max(ms[nod*2],ms[nod*2+1]),rs[nod*2]+ls[nod*2+1]);
	ls[nod]=max(ls[nod*2],sum[nod*2]+ls[nod*2+1]);
	rs[nod]=max(rs[nod*2+1],sum[nod*2+1]+rs[nod*2]);
	sum[nod]=sum[nod*2]+sum[nod*2+1];
}

這樣一來build和update就變得非常常規:

void build(int l,int r,int nod){
	if (l==r){
		sum[nod]=ms[nod]=ls[nod]=rs[nod]=a[l];
		return;
	}
	int mid=(l+r)/2;
	build(l,mid,nod*2);
	build(mid+1,r,nod*2+1);
	pushup(nod);
}

void update(int l,int r,int k,int v,int nod){
	if (l==r){
		sum[nod]=ms[nod]=ls[nod]=rs[nod]=v;
		return;
	}
	int mid=(l+r)/2;
	if (k<=mid) update(l,mid,k,v,nod*2);
	else update(mid+1,r,k,v,nod*2+1);
	pushup(nod);
}

接下來,我們看——query


首先,如果查詢區間在當前區間的左邊,那我們就去左邊找,如果查詢區間在當前區間的右邊,那我們就去右邊找——這是地球人都知道的事情
我們現在要考慮的就是一般在左,一半在右的情況——其實與我們的pushup極像

我們要query的是ms,我們分三種情況討論——把左邊的ms給query出來,把右邊的ms給query出來,然後我們還要把左邊的rs給query,右邊的ls給搞出來加在一起,三個東西取一個max

於是乎,我們又要寫兩個查ls和rs的query,比如查ls的query,當遇到一半在左一半在右的情況時,我們要把左邊的ls給query出來,或者是query左邊的sum 和右邊的ls加一下,取個max

嗯,和pushup一樣,很好理解
BBUUTT,你有沒有注意到一個恐怖的事情,我!們!要!寫!四!個!query!!!,分別求ms,ls,rs,sum
別急,這裏有一個小技巧,我們可以把四個東西在一個query裏求出——做法有兩種

  1. 把四個東西放在一個結構體裏,return一個結構體
  2. 把四個東西放進實參裏,然後傳出

我這裏選擇了第2種:

void query(int l,int r,int ll,int rr,int nod,int &s,int &ans,int &ans_l,int &ans_r){
	if (l==ll && r==rr){
		ans=ms[nod];
		ans_l=ls[nod];
		ans_r=rs[nod];
		s=sum[nod];
		return;
	}
	int mid=(l+r)/2;
	if (rr<=mid) query(l,mid,ll,rr,nod*2,s,ans,ans_l,ans_r);
	else if (ll>mid) query(mid+1,r,ll,rr,nod*2+1,s,ans,ans_l,ans_r);
	else{
		int l_ans,r_ans,l_ans_l,l_ans_r,r_ans_l,r_ans_r,l_s,r_s;
		query(l,mid,ll,mid,nod*2,l_s,l_ans,l_ans_l,l_ans_r);
		query(mid+1,r,mid+1,rr,nod*2+1,r_s,r_ans,r_ans_l,r_ans_r);
		ans=max(max(l_ans,r_ans),l_ans_r+r_ans_l);
		ans_l=max(l_ans_l,l_s+r_ans_l);
		ans_r=max(r_ans_r,r_s+l_ans_r);
		s=l_s+r_s;
	}
}

相信你看變量名看得已經頭暈了,簡單解釋一下:

  • s:我們現在要求的區間的和(實參)
  • ans:我們現在要求的ms(實參)
  • ans_l:我們現在要求的ls(實參)
  • ans_r:我們現在要求的rs(實參)
  • l_XXX:左區間的XXX
  • r_XXX:右區間的XXX

OK,完事


搞定了這道題以後你可以去試試這道題:【SPOJ2916 GSS5】Can you answer these queries V


c++代碼:

#include<cstdio>
#include<iostream>
using namespace std;

const int maxn=50005;
int a[maxn],sum[maxn*4],ms[maxn*4],ls[maxn*4],rs[maxn*4];

int max(int x,int y){
    return x>y?x:y;
}

void pushup(int nod){
    ms[nod]=max(max(ms[nod*2],ms[nod*2+1]),rs[nod*2]+ls[nod*2+1]);
    ls[nod]=max(ls[nod*2],sum[nod*2]+ls[nod*2+1]);
    rs[nod]=max(rs[nod*2+1],sum[nod*2+1]+rs[nod*2]);
    sum[nod]=sum[nod*2]+sum[nod*2+1];
}

void build(int l,int r,int nod){
    if (l==r){
        sum[nod]=ms[nod]=ls[nod]=rs[nod]=a[l];
        return;
    }
    int mid=(l+r)/2;
    build(l,mid,nod*2);
    build(mid+1,r,nod*2+1);
    pushup(nod);
}

void update(int l,int r,int k,int v,int nod){
    if (l==r){
        sum[nod]=ms[nod]=ls[nod]=rs[nod]=v;
        return;
    }
    int mid=(l+r)/2;
    if (k<=mid) update(l,mid,k,v,nod*2);
    else update(mid+1,r,k,v,nod*2+1);
    pushup(nod);
}

void query(int l,int r,int ll,int rr,int nod,int &s,int &ans,int &ans_l,int &ans_r){
    if (l==ll && r==rr){
        ans=ms[nod];
        ans_l=ls[nod];
        ans_r=rs[nod];
        s=sum[nod];
        return;
    }
    int mid=(l+r)/2;
    if (rr<=mid) query(l,mid,ll,rr,nod*2,s,ans,ans_l,ans_r);
    else if (ll>mid) query(mid+1,r,ll,rr,nod*2+1,s,ans,ans_l,ans_r);
    else{
        int l_ans,r_ans,l_ans_l,l_ans_r,r_ans_l,r_ans_r,l_s,r_s;
        query(l,mid,ll,mid,nod*2,l_s,l_ans,l_ans_l,l_ans_r);
        query(mid+1,r,mid+1,rr,nod*2+1,r_s,r_ans,r_ans_l,r_ans_r);
        ans=max(max(l_ans,r_ans),l_ans_r+r_ans_l);
        ans_l=max(l_ans_l,l_s+r_ans_l);
        ans_r=max(r_ans_r,r_s+l_ans_r);
        s=l_s+r_s;
    }
}

int main(){
    int n;
    scanf("%d",&n);
    for (int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    build(1,n,1);
    int m;
    scanf("%d",&m);
    while(m--){
        int k,x,y,ans=0,ansl=0,ansr=0,s=0;
        scanf("%d%d%d",&k,&x,&y);
        if (k==0) update(1,n,x,y,1);
        else{
            query(1,n,x,y,1,s,ans,ansl,ansr);
            printf("%d\n",ans);
        }
    }
    return 0;
}

於XJ機房607

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章