小白逛公园 - Vijos(线段树+一点dp)

描述

小新经常陪小白去公园玩,也就是所谓的遛狗啦…在小新家附近有一条“公园路”,路的一边从南到北依次排着 n n 个公园,小白早就看花了眼,自己也不清楚该去哪些公园玩了。

一开始,小白就根据公园的风景给每个公园打了分-.-。小新为了省事,每次遛狗的时候都会事先规定一个范围,小白只可以选择第a个和第 b b 个公园之间(包括a b b 两个公园)选择连续的一些公园玩。小白当然希望选出的公园的分数总和尽量高咯。同时,由于一些公园的景观会有所改变,所以,小白的打分也可能会有一些变化。
那么,就请你来帮小白选择公园吧。

输入格式

第一行,两个整数N M M ,分别表示表示公园的数量和操作(遛狗或者改变打分)总数。
接下来N行,每行一个整数,依次给出小白 开始时对公园的打分。
接下来 M M 行,每行三个整数。第一个整数K,1或2。 K K =1表示,小新要带小白出去玩,接下来的两个整数a b b 给出了选择公园的范围(1a,bN a a 可以大于b!); K=2 K = 2 表示,小白改变了对某个公园的打分,接下来的两个整数 p p s,表示小白对第 p p 个公园的打分变成了s1pN

其中, 1N5000001M100000 1 ≤ N ≤ 500 000 , 1 ≤ M ≤ 100 000 ,所有打分都是绝对值不超过1000的整数。

输出格式

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

样例输入1
5 3
1 2 -3 4 5
1 2 3
2 2 -1
1 2 3
样例输出1
2
-1
限制

各个测试点2s

传送门!!!

这道题有一点点意思,本来就是奔着线段树去的,但是线段树维护什么还有点懵。一开始想到维护一个区间和,一个从左端最大值,一个从右端最大值。结果查询不知道怎么查,因为问题在于你不知道这一个区间是取左端点还是右端点还是全部要取。这里我们就要引入dp的思想。
对于一个区间,我们都记录它从左端点取,右端点取,区间和,区间最优答案,如果在线段树上 mid>R m i d > R 或者 mid<L m i d < L ,就不用管它,直接return好了,如果它由两个区间构成,我们就考虑转移:

ret1=ques(b[u].lson,l,mid);  
ret2=ques(b[u].rson,mid+1,r);  
ret.left=max(ret1.left,ret1.num+ret2.left);  
ret.right=max(ret2.right,ret2.num+ret1.right);  
ret.num=ret1.num+ret2.num;  
ret.ans=max(ret1.right+ret2.left,max(ret1.ans,ret2.ans));  

这个ret是一个结构体,用于打包四个维护值。
然后单点修改是很简单的,连tag和pushdown都不需要了。

为什么可以这样转移?有时候会突然有点晕,有没有可能wa掉(合并的时候不一定是连续的一段)?思考一下对于上一个区间,它要从两个区间合并,值得注意的是,这两个区间一定是连续的,不可能是断开的。所以正确性是有保障的,可以在看一看上面的转移。

然后还有两个坑点,不保证 a<b a < b ,还有就是这段之间至少要选一个公园,所以对于一个点来说,它的四个维护值是相同的。

然后贴一下代码,个人认为还是挺快的。1513ms。

#include<iostream>  
#include<cstdio>  
#include<cstring>  
using namespace std;  
struct Tree{  
    int num,left,right,ans;  
    int lson,rson,l,r;  
}b[2000005];  

struct node{  
    int num,left,right,ans;  
};  

int n,m,root,cnt;  
int data[500005];  

void update(int u)  
{  
    b[u].num=b[b[u].lson].num+b[b[u].rson].num;  
    b[u].left=max(b[b[u].lson].left,b[b[u].lson].num+b[b[u].rson].left);  
    b[u].right=max(b[b[u].rson].right,b[b[u].rson].num+b[b[u].lson].right);  
    b[u].ans=max(b[b[u].lson].right+b[b[u].rson].left,max(b[b[u].rson].ans,b[b[u].lson].ans));  
}  

void build(int &u,int l,int r)  
{  
    u=++cnt;  
    b[u].l=l;b[u].r=r;  
    if(l==r)  
    {  
        b[u].num=data[l];  
        b[u].ans=b[u].num;  
        b[u].left=b[u].num;  
        b[u].right=b[u].num;  
        return;  
    }  
    int mid=(l+r)/2;  
    build(b[u].lson,l,mid);  
    build(b[u].rson,mid+1,r);  
    update(u);  
}  

void modify(int u,int aim,int x)  
{  
    if(b[u].l==b[u].r)  
    {  
        b[u].num=x;  
        b[u].left=b[u].num;  
        b[u].right=b[u].num;  
        b[u].ans=b[u].num;  
        return;  
    }  
    int mid=(b[u].l+b[u].r)/2;  
    if(aim<=mid)  
      modify(b[u].lson,aim,x);  
    if(aim>mid)  
      modify(b[u].rson,aim,x);  
    update(u);  
}  

node ques(int u,int l,int r)  
{  
    if(l==b[u].l&&r==b[u].r)  
    {  
        node ret;  
        ret.num=b[u].num;  
        ret.left=b[u].left;  
        ret.right=b[u].right;  
        ret.ans=b[u].ans;  
        return ret;  
    }  
    int mid=(b[u].l+b[u].r)/2;  
    if(l>mid) return ques(b[u].rson,l,r);  
    else if(r<=mid) return ques(b[u].lson,l,r);  
    else  
    {  
        node ret1,ret2,ret;  
        ret1=ques(b[u].lson,l,mid);  
        ret2=ques(b[u].rson,mid+1,r);  
        ret.left=max(ret1.left,ret1.num+ret2.left);  
        ret.right=max(ret2.right,ret2.num+ret1.right);  
        ret.num=ret1.num+ret2.num;  
        ret.ans=max(ret1.right+ret2.left,max(ret1.ans,ret2.ans));  
        return ret;  
    }   
}  

int main()  
{  
    scanf("%d%d",&n,&m);  
    for(int i=1;i<=n;i++)  
      scanf("%d",&data[i]);  
    build(root,1,n);  
    for(int i=1;i<=m;i++)  
    {  
        int x;  
        scanf("%d",&x);  
        if(x==1)  
        {  
            int a1,a2;  
            scanf("%d%d",&a1,&a2);  
            if(a1>a2) swap(a1,a2);  
            printf("%d\n",ques(root,a1,a2).ans);  
        }  
        if(x==2)  
        {  
            int a1,a2;  
            scanf("%d%d",&a1,&a2);  
            modify(root,a1,a2);  
        }  
    }   
}  
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值