线段树 信息维护 单点修改你能回答这些问题吗

Posted 鱼竿钓鱼干

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线段树 信息维护 单点修改你能回答这些问题吗相关的知识,希望对你有一定的参考价值。

【线段树 信息维护 单点修改】你能回答这些问题吗

在这里插入图片描述

思路

1单点修改,只需要pushup。
2查询:查询区间内最大连续子段和

一、维护哪些信息

思考:查询区间内最大连续子段和需要存哪些信息
基本的

struct Node{
	int l,r;//区间左右端点
	int tmax;//最大连续子段和	
}

但是我们无法通过左右儿子的tmax得到父节点的tmax。有可能横跨左右子区间。
在这里插入图片描述

可以发现
横跨左右子区间的最大连续子段和=左子区间的最大后缀和右子区间的最大前缀
所以我们还需对维护每个区间的最大后缀和以及最大前缀和

struct Node{
	int l,r;//区间左右端点
	int tmax;//最大连续子段和	
	int lmax,rmax;//最大前缀和,最大后缀和
}

那么我们可以得到tmax的表达式
t m a x = m a x ( l e f t s o n t m a x , r i g h t s o n t m a x , l e f t s o n r m a x + r i g h t s o n l m a x ) tmax=max(left_{son} tmax,right_{son}tmax,left_{son}rmax+right_{son}lmax) tmax=max(leftsontmax,rightsontmax,leftsonrmax+rightsonlmax)

继续思考新增的信息能否通过左右子区间得到。
父区间的最大前缀和可以有左右子区间最大前缀和得到吗
1最大前缀和只在左区间,那么可以得到
2最大前缀和横跨左右区间,光凭目前的信息无法得到。
我们可以发现横跨左右区间的最大前缀和可以表示为左区间的sum+右区间最大前缀和。
所以我们还要维护一个sum

struct Node{
	int l,r;//区间左右端点
	int tmax;//最大连续子段和	
	int lmax,rmax;//最大前缀和,最大后缀和
	int sum;//区间和
}

我们可以得到lmax的表达式
l m a x = m a x ( l e f t s o n l m a x , l e f t s o n s u m + r i g h t s o n l m a x ) lmax=max(left_{son}lmax,left_{son}sum+right_{son}lmax) lmax=max(leftsonlmax,leftsonsum+rightsonlmax)
同理我们可以得到rmax的表达式
r m a x = m a x ( r i g h t s o n r m a x , r i g h t s o n s u m + l e f t s o n r m a x ) rmax=max(right_{son}rmax,right_{son}sum+left_{son}rmax) rmax=max(rightsonrmax,rightsonsum+leftsonrmax)
继续检查新增信息sum可不可以由左右子区间得到,发现可以,那么维护信息足够了。
s u m = l e f t s o n s u m + r i g h t s o n s u m sum=left_{son}sum+right_{son}sum sum=leftsonsum+rightsonsum
总结一下线段树找到要维护信息的流程
在这里插入图片描述

二、写代码!

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

const int N=5e5+10;
int n,m;
int w[N];
struct Node{
    int l,r;
    int tmax;
    int lmax,rmax;
    int sum;
}tr[N*4];
/*维护更新信息*/
void pushup(Node &u,Node &l,Node &r){
    u.sum=l.sum+r.sum;
    u.lmax=max(l.lmax,l.sum+r.lmax);
    u.rmax=max(r.rmax,r.sum+l.rmax);
    u.tmax=max(l.rmax+r.lmax,max(l.tmax,r.tmax));
}

void pushup(int u){
    pushup(tr[u],tr[u<<1],tr[u<<1|1]);
}

void build(int u,int l,int r){
    if(l==r)tr[u]={l,r,w[r],w[r],w[r],w[r]};//叶子节点
    else{
        tr[u]={l,r};//别忘记
        int mid=(l+r)>>1;
        build(u<<1,l,mid),build(u<<1|1,mid+1,r);//递归左右区间
        pushup(u);
    }
}

void modify(int u,int x,int v){
    if(tr[u].l==x&&tr[u].r==x)tr[u]={x,x,v,v,v,v};//叶子节点
    else{
        int mid=(tr[u].l+tr[u].r)>>1;
        if(x<=mid)modify(u<<1,x,v);
        else modify(u<<1|1,x,v);
        pushup(u);
    }
}

Node query(int u,int l,int r){
    if(tr[u].l>=l&&tr[u].r<=r)return tr[u];//区间被包含
    else{
        int mid=(tr[u].l+tr[u].r)>>1;
        if(r<=mid)return query(u<<1,l,r);
        else if(l>mid)return query(u<<1|1,l,r);
        else{
            auto left=query(u<<1,l,r);
            auto right=query(u<<1|1,l,r);
            Node res;
            pushup(res,left,right);//合并左右子区间答案
            return res;
        }
    }
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%d",&w[i]);
    build(1,1,n);
    
    int k,x,y;
    while(m--){
        scanf("%d%d%d",&k,&x,&y);
        if(k==1){
            if(x>y)swap(x,y);
            printf("%d\\n",query(1,x,y).tmax);
        }
        else modify(1,x,y);
    }
    return 0;
}

以上是关于线段树 信息维护 单点修改你能回答这些问题吗的主要内容,如果未能解决你的问题,请参考以下文章

你能回答这些问题吗 (线段树)

线段树

线段树你能回答这问题吗?

浅谈树状数组套主席树

线段树 建树 单点修改 点点/区间查询

题解贪婪大陆