【POJ2750】最大连续和 线段树

题目描述

  给出一个含有N个结点的环,编号分别为1..N,环上的点带有权值(可正可负),现要动态的修改某个点的权值,求每次修改后环上的最大连续和,但不能是整个序列的和。

题目大意

单点修改,求环上的最大和。

数据范围

(4<=N,M<=100000)(不会爆int)

样例输入

5
3 -2 1 2 -5
4
2 -2
5 -5
2 -4
5 -1

样例输出

4
4
3
5

解题思路

  线段树,维护:↓(最大值与最小值是指这一段的和的最大值与最小值)
Max(区间最大值),MaxL(从左端开始的区间最大值),Maxr(从右端开始的区间最大值)
Min(区间最小值),MinL(从左端开始的区间最小值),Minr(从右端开始的区间最小值)
Sum(区间和),MinMin(这个东西我也不确定是否为必须的,以前做没有修改的环上的最大和时用过的,就加上吧233)
  所以对环上最大和的询问的Ans就是(1-n这个区间的最大值)和(Sum-区间最小值)中的最大值A.A

代码

#include <bits/stdc++.h>
using namespace std;
inline int Getint(){int x=0,f=1;char ch=getchar();while('0'>ch||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while('0'<=ch&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;}
int vl[100005],Sum=0,All=0;
struct node{
    int L,r,Max,MaxL,Maxr,Min,MinL,Minr,Sum,MinMin;
}Tree[400005];
void PushUp(int v){
    Tree[v].Max=max(Tree[2*v].Max,Tree[2*v+1].Max);
    Tree[v].Max=max(Tree[2*v].Maxr+Tree[2*v+1].MaxL,Tree[v].Max);
    Tree[v].MaxL=max(Tree[2*v].MaxL,Tree[2*v].Sum+Tree[2*v+1].MaxL);
    Tree[v].Maxr=max(Tree[2*v+1].Maxr,Tree[2*v+1].Sum+Tree[2*v].Maxr);
    Tree[v].Max=max(Tree[v].MaxL,Tree[v].Max);
    Tree[v].Max=max(Tree[v].Maxr,Tree[v].Max);
    Tree[v].Min=min(Tree[2*v].Min,Tree[2*v+1].Min);
    Tree[v].Min=min(Tree[2*v].Minr+Tree[2*v+1].MinL,Tree[v].Min);
    Tree[v].MinL=min(Tree[2*v].MinL,Tree[2*v].Sum+Tree[2*v+1].MinL);
    Tree[v].Minr=min(Tree[2*v+1].Minr,Tree[2*v+1].Sum+Tree[2*v].Minr);
    Tree[v].Min=min(Tree[v].MinL,Tree[v].Min);
    Tree[v].Min=min(Tree[v].Minr,Tree[v].Min);
    Tree[v].Sum=Tree[2*v].Sum+Tree[2*v+1].Sum;
    Tree[v].MinMin=min(Tree[2*v].MinMin,Tree[2*v+1].MinMin);
}
void Build(int v,int L,int r){
    Tree[v]=(node){L,r,-1<<30,-1<<30,-1<<30,1<<30,1<<30,1<<30,0};
    if(L==r)return;
    Build(2*v,L,(L+r)/2);
    Build(2*v+1,(L+r)/2+1,r);
}
void Insert(int v,int pos,int val){
    if(pos<Tree[v].L||Tree[v].r<pos)return;
    if(Tree[v].L==Tree[v].r){
        Tree[v]=(node){Tree[v].L,Tree[v].r,val,val,val,val,val,val,val,val};
        return;
    }
    Insert(2*v,pos,val);
    Insert(2*v+1,pos,val);
    PushUp(v);
}
int n;
void Solve(int pos,int val){
    Sum=Sum-vl[pos]+val;
    All-=vl[pos]<0;
    vl[pos]=val;
    All+=vl[pos]<0;
    Insert(1,pos,val);
    int Max=Tree[1].Max,Min=Tree[1].Min;
    if(All==n)cout<<Max<<"\n";
    else{
        int Ans=max(Max,Sum-Min);
        if(Ans==Tree[1].Sum)Ans-=Tree[1].MinMin;
        cout<<Ans<<"\n";
    }
}
int main(){
    n=Getint();
    Build(1,1,n);
    for(int i=1;i<=n;i++)Sum+=vl[i]=Getint(),All+=vl[i]<0;
    for(int i=1;i<=n;i++)Insert(1,i,vl[i]);
    int q=Getint();
    while(q--){
        int pos=Getint(),val=Getint();
        Solve(pos,val);
    }
    return 0;
}

转载于:https://www.cnblogs.com/Cedric341561/p/6811014.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值