你能回答这些问题吗【线段树+子段和】

你能回答这些问题吗【线段树+子段和】

CH4301Acwing245

题目

给定长度为 N 的数列 A,以及 M 条指令,每条指令可能是以下两种之一:

  1. 1 x y,查询区间 [x,y] 中的最大连续子段和。
  2. 2 x y,把 A[x]改成 y。

对于每个查询指令,输出一个整数表示答案。

输入:

第一行两个整数 N,M。

第二行 N 个整数 A[i]。

接下来 M 行每行 3 个整数 k,x,y,k=1 表示查询(此时如果 x>y,请交换 x,y),k=2 表示修改。

输出:

对于每个查询指令输出一个整数表示答案。

每个答案占一行。

数据范围:

N≤500000,M≤100000
−1000≤A[i]≤1000

样例输入:
5 3
1 2 -3 4 5
1 2 3
2 2 -1
1 3 2
样例输出:
2
-1

思路:

对于这个题目来说,最重要的就是查找区间最大字串了。
那么如果对于一个结点来说,它的最大的值应该是(左孩子的最大字段和、右孩子的最大字段和、左孩子的最右最大和+右孩子的最左最大和)的最大值。
因此对于一个结点来说,除了区间左右端点的值l,r之外还需要如下信息存储一些额外的信息,具体可以看代码了。

代码:

#include<bits/stdc++.h>
using namespace std;

#define int long long
const int N = 5e5+5;

int a[N],n,m;
struct Node{
    int l,r;        //存储区间
    int sum;        //区间和
    int lm,rm;      //左、右最大前缀和
    int ans;        //区间最大子段和
} tree[N << 2];

void build(int p,int l,int r){          //建立结点[l,r]
    tree[p].l = l,tree[p].r = r;        //结点p的区间信息是[l,r]
    if(l == r){                         //如果是叶节点,那么直接初始化
        tree[p].sum = tree[p].lm = tree[p].rm = tree[p].ans = a[l];
        return;
    }
    int lc = p << 1,rc = p << 1 | 1,mid = tree[p].l + tree[p].r >> 1;   //左孩子,右孩子,中点
    build(lc,l,mid);                    //建立左孩子
    build(rc,mid + 1,r);                //建立右孩子

    tree[p].sum = tree[lc].sum + tree[rc].sum;  //结点p的区间和是左孩子的和加上右孩子的和
    tree[p].lm = max(tree[lc].lm,tree[lc].sum + tree[rc].lm);   //左最大
    tree[p].rm = max(tree[rc].rm,tree[rc].sum + tree[lc].rm);   //右最大
    tree[p].ans = max(max(tree[lc].ans,tree[rc].ans),tree[lc].rm + tree[rc].lm);    //最大
}

void update(int p,int x,int y){         //将a[x]改成y
    if(tree[p].l == x && tree[p].r == x){
        tree[p].sum = tree[p].lm = tree[p].rm = tree[p].ans = y;
        return;
    }
    int lc = p << 1,rc = p << 1 | 1,mid = tree[p].l + tree[p].r >> 1;
    if(x <= mid)
        update(lc,x,y);
    else
        update(rc,x,y);

    tree[p].sum = tree[lc].sum + tree[rc].sum;
    tree[p].lm = max(tree[lc].lm,tree[lc].sum + tree[rc].lm);
    tree[p].rm = max(tree[rc].rm,tree[rc].sum + tree[lc].rm);
    tree[p].ans = max(max(tree[lc].ans,tree[rc].ans),tree[lc].rm + tree[rc].lm);
}

Node ask(int p,int l,int r){
    if(tree[p].l == l && tree[p].r == r){
        return tree[p];
    }
    int lc = p << 1,rc = p << 1 | 1,mid = tree[p].l + tree[p].r >> 1;
    if(r <= mid)
        return ask(lc,l,r);
    else if(l > mid)
        return ask(rc,l,r);
    else{
                //新建两个子节点,a和b,然后用a和b去更新要找的[l,r]结点c
        Node a = ask(lc,l,mid),b = ask(rc,mid + 1,r),c;
        c.l = l,c.r = r;
        c.sum = a.sum + b.sum;
        c.lm = max(a.lm,a.sum + b.lm);
        c.rm = max(b.rm,b.sum + a.rm);
        c.ans = max(max(a.ans,b.ans),a.rm + b.lm);
        return c;
    }
}

signed main(){
    cin>>n>>m;
    for(int i = 1;i <= n;++i)
        cin>>a[i];
    build(1,1,n);
    int op,x,y;
    for(int i = 1;i <= m;++i){
        cin>>op>>x>>y;
        if(op == 1){
            if(x > y)
                swap(x,y);
            cout<<ask(1,x,y).ans<<"\n";
        }
        else{
            update(1,x,y);
        }
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Alan_Lowe

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值