zoukankan      html  css  js  c++  java
  • 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
    ",query(1,1,n,x,y).tree);
            }
            else
                update(1,1,n,x,y);
        }
        return 0;
    }
    
  • 相关阅读:
    android onTouchEvent
    AD smart pdf 中文丢失
    c# 定时器
    Android 绘制圆环
    52道Python面试题
    螺旋填数:读入两个整数m,n,输出一个m行n列的矩阵,这个矩阵是1~m*n这些自然数按照右、下、左、上螺旋填入的结果。
    Java经典练习题_Day04
    Java经典练习题_Day05
    Java 面向对象 初探
    Java Array 方法和使用
  • 原文地址:https://www.cnblogs.com/fusiwei/p/13692024.html
Copyright © 2011-2022 走看看