(CDOJ 844 線段樹區間最大連續和 )
阿新 • • 發佈:2019-02-04
Problem
給定一個序列,每個點有相應權值
有兩種操作:
①修改某個點的權值
②查詢給定區間的最大連續區間和
Solution
- 首先要理解清楚題意,是在給定區間內求最大連續子序列。所以暴力的方法很簡單,每次在給定區間上貪心地求即可。複雜度
O(m∗n) - 優化一點可以用線段樹
- 線段樹上維護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;
}