VIJOS-P1083 小白逛公园

VIJOS-P1083 小白逛公园

洛谷传送门

JDOJ传送门

Description

​ 小新经常陪小白去公园玩,也就是所谓的遛狗啦…在小新家附近有一条“公园路”,路的一边从南到北依次排着n个公园,小白早就看花了眼,自己也不清楚该去哪些公园玩了。 一开始,小白就根据公园的风景给每个公园打了分。小新为了省事,每次遛狗的时候都会事先规定一个范围,小白只可以选择第a个和第b个公园之间(包括a、b两个公园)选择连续的一些公园玩。小白当然希望选出的公园的分数总和尽量高咯。同时,由于一些公园的景观会有所改变,所以,小白的打分也可能会有一些变化。 那么,就请你来帮小白选择公园吧。

Input

​ 第一行,两个整数N和M,分别表示表示公园的数量和操作(遛狗或者改变打分)总数。 接下来N行,每行一个整数,依次给出小白 开始时对公园的打分。 接下来M行,每行三个整数。第一个整数K,1或2。K=1表示,小新要带小白出去玩,接下来的两个整数a和b给出了选择公园的范围(1≤a,b≤N);K=2表示,小白改变了对某个公园的打分,接下来的两个整数p和s,表示小白对第p个公园的打分变成了s(1≤p≤N)。 其中,1≤N≤500 000,1≤M≤100 000,所有打分都是绝对值不超过1000的整数。

Output

​ 小白每出去玩一次,都对应输出一行,只包含一个整数,表示小白可以选出的公园得分和的最大值。

Sample Input

5 3 1 2 -3 4 5 1 2 3 2 2 -1 1 2 3

Sample Output

2 -1


题解:

题面不难理解,就是简单的动态单点修改+区间最大子段和。

但是如何维护区间最大子段和呢?DP?DP的复杂度是\(O(N)\)的,显然不行,而且,DP也不支持动态的修改。

那么我们当然要选择区间问题的好帮手:区间树了(线段树的别名)。

当然,你得知道线段树是什么,如果不会请走这边:

线段树详解

在鄙人的理解中,线段树作为一种数据结构,其思想是分治的树形DP。就拿最简单的线段树来讲,动态修改+维护区间最大值。我们可以把线段树的每个节点理解成树形DP的状态:表示当前节点维护的区间\([L,R]\)的最大值,那么显而易见,上层节点可以由它的两个儿子推出,符合无后效性和最优子结构。那么这就是一个树形DP嘛!

那为什么又有分治思想呢?因为当前节点维护的区间是二分的,这就是一种分而治之的思想。

那么,除了线段树支持的基本三大操作:求和、求最大/最小值之外,其他的区间维护问题就也可以使用线段树解决。运用的还是分治+树形DP的思想。即只需要考虑这么一个问题:如何设置状态才能使得线段树父子节点之间可以上推。

回到这个问题。动态修改+区间最大子段和。最大子段和怎么维护呀?首先,我想到就粗暴地设置状态为:”\(tree[pos]\)表示本区间的最大子段和。“但是显然的是,这种状态是不支持上推的,它的正确性无法保证,因为可能会有最大子段和是跨过这个区间的中点的,所以不能以中点分成两个儿子来推。

怎么办呢?我们需要合理地设置一个或多个状态,使得这些状态的父子关系可以平滑向上更新。

那么我们考虑这么设:

设置:

\(tree[pos]\)表示当前节点表示区间的最大子段和

\(lmax[pos]\)表示当前节点表示区间一定包含左端点的最大子段和

\(rmax[pos]\)表示当前节点表示区间一定包含右端点的最大子段和

\(sum[pos]\)表示当前节点表示区间的总和

为什么这么设置呢?仍然回到上面讲到的问题:为什么不能只设\(tree[pos]\)表示答案呢?因为可能存在最大子段和的区间横跨中点的情况出现。那么如何才能把这种情况也包含进去呢?简单啊,把这个区间拆开就好了,于是就有了以上\(lmax,rmax\)的状态。

于是,我们可以平滑的转移:

\(lson/rson\)为当前节点\(pos\)的左右儿子,有:

\(tree[pos]=\max(tree[lson],tree[rson],lmax[rson]+rmax[lson])\)

然后我们还需要考虑如何维护\(lmax[],rmax[]\),也很简单:

\(lmax[pos]=\max(sum[lson]+lmax[rson],lmax[lson])\)

\(rmax[pos]=\max(rmax[rson],rmax[lson]+sum[rson])\)

问题解决:

代码:

#include<cstdio>
#include<algorithm>
#define lson pos<<1
#define rson pos<<1|1
using namespace std;
const int maxn=5*1e5+10;
int n,m;
int w[maxn];
struct node
{
    int tree,lmax,rmax,sum;
}a[maxn<<2];
void pushup(int pos)
{
    a[pos].sum=a[lson].sum+a[rson].sum;
    a[pos].lmax=max(a[lson].lmax,a[lson].sum+a[rson].lmax);
    a[pos].rmax=max(a[rson].rmax,a[rson].sum+a[lson].rmax);
    a[pos].tree=max(max(a[lson].tree,a[rson].tree),a[lson].rmax+a[rson].lmax);
}
void build(int pos,int l,int r)
{
    int mid=(l+r)>>1;
    if(l==r)
    {
        a[pos].tree=a[pos].lmax=a[pos].rmax=a[pos].sum=w[l];
        return;
    }
    build(lson,l,mid);
    build(rson,mid+1,r);
    pushup(pos);
}
void update(int pos,int l,int r,int x,int k)
{
    int mid=(l+r)>>1;
    if(l==r)
    {
        a[pos].tree=a[pos].lmax=a[pos].rmax=a[pos].sum=k;
        return;
    }
    if(x<=mid)
        update(lson,l,mid,x,k);
    else
        update(rson,mid+1,r,x,k);
    pushup(pos);
}
node query(int pos,int l,int r,int x,int y)
{
    node ret;
    int mid=(l+r)>>1;
    if(x<=l && r<=y)
        return a[pos];
    else if(y<=mid)
        ret=query(lson,l,mid,x,y);
    else if(x>mid)
        ret=query(rson,mid+1,r,x,y);
    else
    {
        node p=query(lson,l,mid,x,y);
        node q=query(rson,mid+1,r,x,y);
        ret.sum=p.sum+q.sum;
        ret.lmax=max(p.lmax,p.sum+q.lmax);
        ret.rmax=max(q.rmax,q.sum+p.rmax);
        ret.tree=max(max(p.tree,q.tree),p.rmax+q.lmax);
    }
    return ret;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%d",&w[i]);
    build(1,1,n);
    while(m--)
    {
        int k,x,y;
        scanf("%d%d%d",&k,&x,&y);
        if(k==1)
        {
            if(x>y)
                swap(x,y);
            printf("%d\n",query(1,1,n,x,y).tree);
        }
        else
            update(1,1,n,x,y);
    }
    return 0;
}
posted @ 2020-09-18 16:34  Seaway-Fu  阅读(141)  评论(0编辑  收藏  举报