前缀和+贪心+线段树——cf893D

/*
贪心策略:
    如果检查的那天不用加,那就不加
    反之加上尽可能多的钱
        如何确定这个值?
            处理出前缀和,第i天可以加的钱=d-[i+1,n]天里前缀和的最大值
        加上这个值后,要再一次更新前缀和数组,用线段树维护前缀和数组即可      
*/
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define N 200005

ll n,d,a[N],sum[N];
vector<int>v;

#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
ll lazy[N<<2],Max[N<<2];
void pushdown(int rt){
    lazy[rt<<1]+=lazy[rt];
    lazy[rt<<1|1]+=lazy[rt];
    Max[rt<<1]+=lazy[rt];
    Max[rt<<1|1]+=lazy[rt];
    lazy[rt]=0;
}
void build(int l,int r,int rt){
    if(l==r){Max[rt]=sum[l];return;}
    int m=l+r>>1;
    build(lson);build(rson);
    Max[rt]=max(Max[rt<<1],Max[rt<<1|1]); 
} 
void update(int L,int R,int v,int l,int r,int rt){
    if(L<=l && R>=r){
        Max[rt]+=v;lazy[rt]+=v;return;
    }
    pushdown(rt);
    int m=l+r>>1;
    if(L<=m)update(L,R,v,lson);
    if(R>m)update(L,R,v,rson);
    Max[rt]=max(Max[rt<<1],Max[rt<<1|1]); 
}
ll query(int L,int R,int l,int r,int rt){
    if(L<=l && R>=r)return Max[rt];
    int m=l+r>>1;
    pushdown(rt);
    ll res=-0x3f3f3f3f3f3f3f3f;
    if(L<=m)res=max(res,query(L,R,lson));
    if(R>m)res=max(res,query(L,R,rson));
    return res;
}
int main(){
    cin>>n>>d;
    for(int i=1;i<=n;i++)cin>>a[i];
    for(int i=1;i<=n;i++){
        sum[i]=sum[i-1]+a[i];
        if(sum[i]>d){
            cout<<-1;return 0;
        }
    }
    build(1,n,1);
    for(int i=1;i<=n;i++)
        if(a[i]==0)v.push_back(i);
    
    int ans=0;
    for(auto pos:v){
        ll cur=query(pos,pos,1,n,1);
        if(cur>=0)continue;
        ans++;
        ll mx=query(pos,n,1,n,1);
        ll dif=d-mx;
        if(dif+cur<0){
            cout<<-1<<'
';return 0;
        }
        update(pos+1,n,dif,1,n,1);
    }
    
    cout<<ans<<'
';
    
}
原文地址:https://www.cnblogs.com/zsben991126/p/12296666.html