线段树优化dp

链接:https://ac.nowcoder.com/acm/contest/917/E
来源:牛客网

。等离子炮有n个操作信号,第i个操作信号的强度为b[i]。总体强度为各操作信号的强度之和。

由于有些信号太弱了了 (强度<0),水宝宝想把它们删除。但是水宝宝自己不会删除信号,所以他找来了同船的队友帮忙。

有 m位队友,第ii 位队友只会删除编号在 L[i] 和 R[i]之间的信号,且每删除一个信号,花费 C[i]格能量。飞船一共有 k格能量,问他在请队友删除完信号后,总体强度最大是多少。
 
 
 
注:本系列题不按难度排序哦

 

输入描述:

输入格式:

第一行包含三个正整数 n,k,m

第二行包含 n个正整数 b1,b2,⋯,bn,表示各信号的强度。

接下来 m 行,每行三个正整数Li,Ri,Ci,表示一个队友的属性。

输出描述:

输出格式:

输出一行一个整数,表示最大的信号强度
示例1

输入

复制 
5 10 5
10 -2 -5 7 -10
1 1 5
2 4 10
4 4 12
3 4 10
1 5 15

输出

复制 
5

说明

样例解释:花费10的代价除掉a[3],答案即为10+7-10-2=5

备注:

对于 100% 的数据,1≤n,m≤10^5;1≤k≤500;1≤C[i]≤500;1≤L[i]≤R[i]≤n;-10^9≤b[i]≤10^9
这个题目就是先找a[i]小于0的点就是i值
然后再用线段树寻找在这个区间内的价值最小的那个区间然后再跑一个01背包
注意一下这个区间的跟新和查找

#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=5e5+100;
const int inf=0x3f3f3f3f;
typedef long long ll;
struct node{
    int l,r;
    int mi;
}t[maxn*4];
int a[maxn],b[maxn],c[maxn];
int dp[maxn];
void build(int p,int l,int r){
    t[p].l=l;
    t[p].r=r;
    t[p].mi=inf;
    if(l!=r){
        int mid=(t[p].l+t[p].r)/2;
        build(2*p,l,mid);
        build(2*p+1,mid+1,r);
    }
} 
void update(int p,int l,int r,int x){
    if(t[p].l>=l&&t[p].r<=r){//注意 
        t[p].mi=min(t[p].mi,x);
        return ;
    }
    int mid=(t[p].l+t[p].r)/2;
    if(l<=mid){
        update(2*p,l,r,x);
    } 
    if(r>mid){
        update(2*p+1,l,r,x);
    }
}
int query(int p,int x){
    if(t[p].l==t[p].r){
        return t[p].mi;
    }
    int mid=(t[p].l+t[p].r)/2;
    if(x<=mid){
        return min(query(2*p,x),t[p].mi);//如果t[p].mi有值的话,就说明一定有一个区间
        //包含t[p].l和t[p].r,所以要跟新 
    }
    else{
        return min(query(2*p+1,x),t[p].mi);
    }
} 
int main(){
    int n,m,k;
    cin>>n>>k>>m;
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    build(1,1,n);
    int l,r,x;
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&l,&r,&x);
        update(1,l,r,x);
    }
    ll ans=0; 
    int cnt=0;
    for(int i=1;i<=n;i++){
        ans+=a[i];
        if(a[i]<0){
            b[++cnt]=-a[i];
            c[cnt]=query(1,i);//找这个点的最小价值的区间 
        }
    }
    for(int i=1;i<=cnt;i++){
        for(int j=k;j>=c[i];j--){
            dp[j]=max(dp[j],dp[j-c[i]]+b[i]);
        }
    }
    printf("%lld\n", ans+dp[k]);
}

 

 
posted @ 2021-01-24 17:35  lipu123  阅读(60)  评论(0编辑  收藏  举报