求区间最大连续子段和.new(线段树)

这个故事是这样的:
在n年前,憨憨博主在参加省队集训的时候,第一次接触到了这个问题:区间最大连续子段和,
于是博主就开始一阵恶补,get了这种新技能

然而在夏季的一个宁静的午后,博主的CSDN突然接到消息通知,发现好心的大佬给我指出了我写的代码的不足,憨憨博主非常惭愧。。。

前端时间都在忙着大学的各种事项,没有抽出时间来修正这个错误
十一假期留校,突然想起来了我好像还有个大坑没有填,于是就上网查看了一下
发现竟然还有小伙伴在解决这一问题的时候引用的是我的blog。。。误人子弟啊~~
废话不多说,赶紧献上崭新的解题姿态~

(引用我原先blog的小伙伴也不用慌,我会把链接直接指向这篇blog的~)


首先,这个“区间最大子段和”是个什么玩意呢

题目给出 n n 个数, q q 次操作,每次改一个值,或者询问区间 [ L , R ] [L,R] 内最大的连续子段和

哎呦,感觉很像线段树的题目哦
毕竟我们是会用线段树求区间最大(小)值的
但是现在题目要求的是最大子段和,问题的难度就上升了一个数量级

线段树需要维护的是:
[ x , y ] [x,y] 内的最大子段和 m s ms
[ x , y ] [x,y] 的区间和 s s
[ x , y ] [x,y] 内的紧靠左端点的最大子段和 l s ls
[ x , y ] [x,y] 内的紧靠右端点的最大子段和 r s rs

困难就是, u p d a t e update a s k ( L , R ) ask(L,R) 询问 [ L , R ] [L,R] 区间内的最大子段和
那我们一步一步来,从维护说起

s s 的维护很常规,
l s ls: 有两种情况:

  1. 该区间内的 l s ls 是ta左儿子的 l s ls
  2. 该区间内的 l s ls 是左儿子的 s s +右儿子的 l s ls

同理, r s rs: 有两种情况:

  1. 该区间内的 r s rs 是ta右儿子的 r s rs
  2. 该区间内的 r s rs 是右儿子的 s s +左儿子的 r s rs

m s ms 有三种情况:

  1. 该区间内的 m s ms 是左儿子的 m s ms
  2. 该区间内的 m s ms 是右儿子的 m s ms
  3. 该区间内的 m s ms 是左儿子的 r s + rs+ 右儿子的 l s ls

在这里插入图片描述

void pushup(int bh) {
    int lson=bh<<1;
    int rson=bh<<1|1;
    tree[bh].s=tree[lson].s+tree[rson].s;
    tree[bh].ls=max(tree[lson].ls,tree[lson].s+tree[rson].ls);
    tree[bh].rs=max(tree[rson].rs,tree[rson].s+tree[lson].rs);
    tree[bh].ms=max(max(tree[lson].ms,tree[rson].ms),tree[lson].rs+tree[rson].ls);
}

那询问呢~

其实道理是一样哒
[ L , R ] [L,R] 内的区间最大子段和分以下几种情况:
1.独立的存在于左儿子或右儿子中
2.左儿子的 r s rs +右儿子的 l s ls
然而如果 [ L , R ] [L,R] 在线段树中是一个节点(我们单独维护过),那我们直接 r e t u r n return m s ms 就好啦

下面给出完整代码

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#define LL long long

using namespace std;

const int M=10000;
struct node{
    LL s,ls,rs,ms;
}tree[M<<2];

void pushup(int bh) {
    int lson=bh<<1;
    int rson=bh<<1|1;
    tree[bh].s=tree[lson].s+tree[rson].s;
    tree[bh].ls=max(tree[lson].ls,tree[lson].s+tree[rson].ls);
    tree[bh].rs=max(tree[rson].rs,tree[rson].s+tree[lson].rs);
    tree[bh].ms=max(max(tree[lson].ms,tree[rson].ms),tree[lson].rs+tree[rson].ls);
}

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

void update(int x,int z,int L,int R,int bh) {
    if (L==R) {
        tree[bh].ls=tree[bh].rs=tree[bh].ms=tree[bh].s=z;
        return;
    }
    int mid=(L+R)>>1;
    if (x<=mid) update(x,z,L,mid,bh<<1);
    else update(x,z,mid+1,R,bh<<1|1);
    pushup(bh);
}

node askans(int x,int y,int L,int R,int bh) {
    if (x<=L&&y>=R) return tree[bh];   //完全包含
    int mid=(L+R)>>1;
    node f1,f2,T;
    T.s=0;
    if (y<=mid)   //全部都在左儿子
        T=askans(x,y,L,mid,bh<<1);
    if (x>mid)     //全部都在右儿子
        T=askans(x,y,mid+1,R,bh<<1|1);
    if (x<=mid&&y>mid) {     //询问区间被拆成两部分
        f1=askans(x,y,L,mid,bh<<1);
        f2=askans(x,y,mid+1,R,bh<<1|1);
  
        T.s=f1.s+f2.s;
        T.ls=max(f1.ls,f1.s+f2.ls);
        T.rs=max(f2.rs,f2.s+f1.rs);
        T.ms=max(max(f1.ms,f2.ms),f1.rs+f2.ls);
 }
    return T;
}

int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    build(1,n,1);
    while (m--) {
        int typ,x,y;
        scanf("%d%d%d",&typ,&x,&y);
        if (typ==1) {
            if (x>y) swap(x,y);
            node ans=askans(x,y,1,n,1);
            printf("%d\n",ans.ms);
        }
        else update(x,y,1,n,1);
    }
    //system("pause");
    return 0;
}

Tip

第一次用VS写代码,有点小费劲
在上传代码的时候,不知道是CSDN的锅还是我的笔记本的锅
在复制粘贴的时候如果代码中出现空行就会导致我的blog内容全部丢失
所以花了好长的时间才完成了排版QwQ

后来发现是浏览器的问题,微软自带的浏览器(Microsoft Edge)对Markdown的兼容性不好,换成Chrome就OK啦

关于VS有自动格式化的问题
缩进排版什么的确实有
但是如果用Edge上传(复制粘贴),所有的排版就会全部乱掉,真的惨啊。。。

发布了941 篇原创文章 · 获赞 192 · 访问量 32万+

猜你喜欢

转载自blog.csdn.net/wu_tongtong/article/details/101998135