我们登上的并非我们所|

XichenOC

园龄:1个月粉丝:4关注:0

2025-01-20 19:31阅读: 3评论: 0推荐: 0

P4513 小白逛公园

P4513 小白逛公园

题目翻译:

给定一个长度为\(n\)初始序列,并进行\(m\)次询问,求一个区间类的最大连续子序列的大小,或者单点修改。

思路:

看到单点修改和区间查询可以很容易想到线段树,那如何用线段树来维护最大连续子序列。我们考虑给线段树维护以下参数:

\[$lm,rm,sum,mx$ \]

\(lm\)指当前区间从左边开始的最大连续序列,而\(rm\)指的是从右边开始。\(sum\)指当前序列的的和,而\(mx\)指当前序列的最大连续序列,也就是\(ans\)。考虑如何往上推:

\(1.\)一个区间的从左的最大连续序列可能是它左儿子的从左开始的最大连续序列,也有可能是最儿子的和在加上右儿子的从左开始的最大连续序列,而右边同理:

\[$tr[p].lm=max(tr[lc].lm,tr[lc].sum+tr[rc].lm);$ \]

\[$tr[p].rm=max(tr[rc].rm,tr[rc].sum+tr[lc].rm);$ \]


\(2.\)若一个区间的最大连续子序列可以是它左儿子的或者右儿子的,但也有可能是做儿子的右最长加上右儿子的左最长,因此关系为:

\[$tr[p].mx=max(max(tr[lc].mx,tr[rc].mx),tr[rc].lm+tr[lc].rm);$ \]


\(3.\)和就不用说,就是左右儿子和之和:

\[$tr[p].sum=tr[lc].sum+tr[rc].sum;$ \]

这样就得到了根节点和左右儿子的关系:

void push_up(int p){
    tr[p].sum=tr[lc].sum+tr[rc].sum;
    tr[p].lm=max(tr[lc].lm,tr[lc].sum+tr[rc].lm);
    tr[p].rm=max(tr[rc].rm,tr[rc].sum+tr[lc].rm);
    tr[p].mx=max(max(tr[lc].mx,tr[rc].mx),tr[rc].lm+tr[lc].rm);
}

后面就不用说,普通的单点修改和区间查询,感觉蓝有点过头了

#include<bits/stdc++.h>
#define int long long
#define lc p<<1
#define rc p<<1|1
using namespace std;
const int N=5e5+10;
struct Tree{
    int lm,rm,mx,sum;
}tr[N*4];
int a[N];
void push_up(int p){
    tr[p].sum=tr[lc].sum+tr[rc].sum;
    tr[p].lm=max(tr[lc].lm,tr[lc].sum+tr[rc].lm);
    tr[p].rm=max(tr[rc].rm,tr[rc].sum+tr[lc].rm);
    tr[p].mx=max(max(tr[lc].mx,tr[rc].mx),tr[rc].lm+tr[lc].rm);
}
void build(int p,int l,int r){
    if(l==r){
        tr[p].lm=tr[p].rm=tr[p].mx=tr[p].sum=a[l];
        return;
    }
    int mid=(l+r)>>1;
    build(lc,l,mid);
    build(rc,mid+1,r);
    push_up(p);
}
Tree query(int p,int l,int r,int ll,int rr){
    if(ll<=l&&r<=rr){
        return tr[p];
    }
    int mid=(l+r)>>1;
    if(ll>mid)return query(rc,mid+1,r,ll,rr);
    else{
        if(rr<=mid)return query(lc,l,mid,ll,rr);
        else{
            Tree res,lt=query(lc,l,mid,ll,rr),rt=query(rc,mid+1,r,ll,rr);
            res.lm=max(lt.lm,lt.sum+rt.lm);
            res.rm=max(rt.rm,rt.sum+lt.rm);
            res.mx=max(max(lt.mx,rt.mx),lt.rm+rt.lm);
            res.sum=lt.sum+rt.sum;
            return res;
        }
    }
}
void update(int p,int l,int r,int k,int num){
    if(l==r){
        //cout<<l<<endl;
        tr[p].lm=tr[p].rm=tr[p].mx=tr[p].sum=num;
        return;
    }
    int mid=(l+r)>>1;
    if(k<=mid)update(lc,l,mid,k,num);
    else update(rc,mid+1,r,k,num);
    push_up(p);
}
signed main(){
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    build(1,1,n);
    while(m--){
        int k;
        cin>>k;
        if(k==1){
            int l,r;
            cin>>l>>r;
            if(l>r)swap(l,r);
            cout<<query(1,1,n,l,r).mx<<endl;
        }
        if(k==2){
            int k,num;
            cin>>k>>num;
            update(1,1,n,k,num);
        }
    }
}

线段树讲解

本文作者:XichenOC

本文链接:https://www.cnblogs.com/XichenOC/p/18682395

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   XichenOC  阅读(3)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起