牛客练习赛56D.小翔和泰拉瑞亚(线段树+思维)

题目链接:https://ac.nowcoder.com/acm/contest/3566/D

题目大意:

给定一个n元素数组,你有m个操作,每次操作可以选择一个区间[li,ri],将这个区间内的数减少vi,你可以选择其中一些进行操作,问你最后可以得到的最大值与最小值的差是多少?

思路:

因为每个操作是对于区间而言,我们不可能去遍历每个区间,所以需要更好的策略。

又因为题中说的是最大值与最小值的差,所以我们可以考虑固定一端,选择另一端。

于是,我们枚举每个位置i,然后选择操作,让i尽可能的小(也就是如果区间x包括了点i,我们就选择区间x),这样点i能够到达理想的最小值,此时我们再查询整个区间的最大值,就是一种可行的答案了,最后答案取所以位置的最大值就完了。

因为一个点可能被很多个区间覆盖,我们并不是暴力加,在从左往右遍历的过程中,在每个区间的左端点l插入这个区间,在区间的右端点r+1处删除这个区间,这样每个区间只会被update两次,就能更新到每个位置i啦。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2e5+10;
struct TreeNode{
    int l,r;
    ll maxx;
    ll minn;
    ll lazy;
}Tree[maxn<<2];
ll a[maxn];
void push_up(int root){
    Tree[root].maxx=max(Tree[root<<1].maxx,Tree[root<<1|1].maxx);
    Tree[root].minn=min(Tree[root<<1].minn,Tree[root<<1|1].minn);
}
void Build(int root,int l,int r){
    Tree[root].l=l,Tree[root].r=r;
    if(l==r){
        Tree[root].maxx=Tree[root].minn=a[l];
        Tree[root].lazy=0;
        return ;
    }
    int mid=(l+r)>>1;
    Build(root<<1,l,mid);
    Build(root<<1|1,mid+1,r);
    push_up(root);
}
void push_down(int root){
    if(Tree[root].lazy){
        Tree[root<<1].lazy+=Tree[root].lazy;
        Tree[root<<1|1].lazy+=Tree[root].lazy;
        Tree[root<<1].minn+=Tree[root].lazy;
        Tree[root<<1|1].maxx+=Tree[root].lazy;
        Tree[root<<1|1].minn+=Tree[root].lazy;
        Tree[root<<1].maxx+=Tree[root].lazy;
        Tree[root].lazy=0;
    }
}
void update(int root,int l,int r,int v){
    if(Tree[root].l>=l&&Tree[root].r<=r){
        Tree[root].minn+=v;
        Tree[root].maxx+=v;
        Tree[root].lazy+=v;
        return ;
    }
    push_down(root);
    int mid=(Tree[root].l+Tree[root].r)>>1;
    if(r<=mid){
        update(root<<1,l,r,v);
    }
    else if(l>mid){
        update(root<<1|1,l,r,v);
    }
    else{
        update(root<<1,l,mid,v);
        update(root<<1|1,mid+1,r,v);
    }
    push_up(root);
}
ll query(int root,int l,int r,int opt){
    if(Tree[root].l>=l&&Tree[root].r<=r){
        return opt==1?Tree[root].maxx:Tree[root].minn;
    }
    push_down(root);
    int mid=(Tree[root].l+Tree[root].r)>>1;
    if(r<=mid){
        return query(root<<1,l,r,opt);
    }
    else if(l>mid){
        return query(root<<1|1,l,r,opt);
    }
    else{
        if(opt==1){
            return max(query(root<<1,l,mid,opt),query(root<<1|1,mid+1,r,opt));
        }
        else{
            return min(query(root<<1,l,mid,opt),query(root<<1|1,mid+1,r,opt));
        }
    }
}
struct Seg{
    int l,r;
    ll val;
}seg[maxn];
vector<int>L[maxn];
vector<int>R[maxn];
int cmp(const Seg a,const Seg b){
    return a.l<b.l||(a.l==b.l&&a.r<b.r);
}

//快读

inline int Read()
{
    int res=0,ch,flag=0;
    if((ch=getchar())=='-')
        flag=1;
    else if(ch>='0'&&ch<='9')
        res=ch-'0';
    while((ch=getchar())>='0'&&ch<='9')
        res=res*10+ch-'0';
    return flag?-res:res;
}

inline ll Readll()
{
    ll res=0,ch,flag=0;
    if((ch=getchar())=='-')
        flag=1;
    else if(ch>='0'&&ch<='9')
        res=ch-'0';
    while((ch=getchar())>='0'&&ch<='9')
        res=res*10+ch-'0';
    return flag?-res:res;
}

signed main(){
    int n,m;
    n=Read(),m=Read();
    for(int i=1;i<=n;i++){
        a[i]=Readll();
    }
    Build(1,1,n);
    for(int i=1;i<=m;i++){
        seg[i].l=Read(),seg[i].r=Read(),seg[i].val=Readll();
    }
    sort(seg+1,seg+m+1,cmp);
    for(int i=1;i<=m;i++){
        L[seg[i].l].push_back(i);
        R[seg[i].r+1].push_back(i);
    }
    ll ans=0;
    for(int i=1;i<=n;i++){
        for(int j=0;j<L[i].size();j++){
            update(1,seg[L[i][j]].l,seg[L[i][j]].r,-seg[L[i][j]].val);
        }
        for(int j=0;j<R[i].size();j++){
            update(1,seg[R[i][j]].l,seg[R[i][j]].r,seg[R[i][j]].val);
        }
        ll now=query(1,i,i,2);
        ll maxx=query(1,1,n,1);
        ans=max(ans,maxx-now);
    }
    printf("%lld\n",ans);
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值