其他分享
首页 > 其他分享> > 【做题笔记】洛谷P4513小白逛公园

【做题笔记】洛谷P4513小白逛公园

作者:互联网

很水的紫题

题目大意

给您一个长度为 \(n\) 的序列,您需要写一个数据结构,支持以下操作

  1. 查询 \([l,r]\) 的最大字段和;
  2. 把 \(a_p\) 变成 \(s\) 。

solution

显然考虑线段树。

对于操作二,更改 \(a_p\) 在线段树中会影响到的结点即可。

单独考虑操作一。

其实有一种 \(\mathcal{O}(n)\) 的求最大子段和的做法,但本题需要一个 \(\mathcal{O}(logn)\) 的做法。

放个图:

3WzF0J.png

对于这样一个区间,她的最大子段和只有三种可能:完全在左区间,完全在右区间,或者是贯穿了左右区间。

所以假如现在知道了左区间的最大子段和、右区间的最大子段和,那么就可以从而推出整个的最大子段和。

所以这一整个区间的最大子段和就是上述三种可能的最大值。

那么现在问题聚焦在了如何求左区间的最大子段和、右区间的最大子段和。

以左区间为例,把左区间再次一分为二,现在要求左区间的最大子段和:

3WzqgK.png

这种情况也就是左区间的最大子段和

3fSAKS.png

这种情况也就是左区间和+右区间左半边最大子段和

然后不断递归就能搞到答案了。

右区间同理。

说到底,仍然是递归的思想。

有一些细节,见代码:

#include <iostream>
#include <stdio.h>
#include <math.h>
#define DEBUG printf("This is OK\n")

using namespace std;

int n,q,a[5000100];

struct SegmentTree
{
    int l,r;
    long long lmax,rmax,ans,sum;
    #define l(x) t[x].l
    #define r(x) t[x].r
    #define lmax(x) t[x].lmax //lmax是左区间最大子段和
    #define rmax(x) t[x].rmax //rmax是右区间最大子段和
    #define ans(x) t[x].ans //ans是整个 [l,r] 的最大子段和
    #define sum(x) t[x].sum
};
SegmentTree t[4*5000010];

void merge(int p) //合并信息
{
    sum(p)=sum(p*2)+sum(p*2+1); //区间和
    lmax(p)=max(lmax(p*2),lmax(p*2+1)+sum(p*2)); 
    rmax(p)=max(rmax(p*2+1),rmax(p*2)+sum(p*2+1));
    ans(p)=max(max(ans(p*2),ans(p*2+1)),lmax(p*2+1)+rmax(p*2));
    //以上合并信息同思路
}

void build(int p,int l,int r)
{
    l(p)=l,r(p)=r;
    if(l==r) {sum(p)=lmax(p)=rmax(p)=ans(p)=a[l];return ;}
    int mid=(l+r)>>1;
    build(p*2,l,mid);
    build(p*2+1,mid+1,r);
    merge(p); //合并信息
}

void change(int p,int x,int v)
{
    if(l(p)==r(p))
    {
        sum(p)=lmax(p)=rmax(p)=ans(p)=v;
        return ;
    }
    int mid=(l(p)+r(p))>>1;
    if(x<=mid) change(p*2,x,v);
    else change(p*2+1,x,v);
    merge(p); //进行了更改,更新信息
}

SegmentTree ask(int p,int l,int r) //由于这里需要涉及多个结构体信息,所以直接把ask函数变成一个结构体函数
{
    if(l<=l(p)&&r>=r(p)) return t[p];
    int mid=(l(p)+r(p))>>1;
    if(r<=mid) return ask(p*2,l,r); //如果右端点都在左半段,那直接去查左半段就好了
    else if (l>mid) return ask(p*2+1,l,r); //同上
    else //既有右端点,又有左端点
    {
        SegmentTree x=ask(p*2,l,r),y=ask(p*2+1,l,r),node;
        //x,y是左、右区间的信息
        node.sum=x.sum+y.sum;
        node.lmax=max(x.lmax,x.sum+y.lmax);
        node.rmax=max(y.rmax,y.sum+x.rmax);
        node.ans=max(max(x.ans,y.ans),x.rmax+y.lmax); //和那个meger函数一样的思路。
        return node;
    }
}

inline int read()
{
    int s=0,w=1; char ch=getchar();
    while(ch<'0'||ch>'9') {if(ch=='-') w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') s=s*10+ch-'0' , ch=getchar();
    return s*w;
}

int main()
{
    n=read(),q=read();
    for(int i=1;i<=n;i++) a[i]=read();
    build(1,1,n);
    //DEBUG;
    while(q--)
    {
        int K;
        K=read();
        if(K==1)
        {
            int l,r;
            l=read(),r=read();
            if(l>r) swap(l,r);
            SegmentTree ans=ask(1,l,r);
            cout<<ans.ans<<"\n";
        }
        else
        {
            int p,s;
            p=read(),s=read();
            change(1,p,s);
        }
    }
    return 0;
}

标签:洛谷,子段,int,sum,rmax,lmax,ans,P4513,逛公园
来源: https://www.cnblogs.com/BlueInRed/p/12404486.html