luoguP5653 基础最优化练习题 拆系数+贪心

直接算不好算,考虑拆开系数来算贡献.      

对于 $b_{i}w_{i}$,可以看成 $1$ ~ $i$ 中每走一步就会产生 $w_{i}$ 的贡献,也就说 $i$ 的贡献就是 $i$ 的后缀和.   

那么问题可以转化为:   

有 $n$ 个元素,每个元素可以选 $[-k,k]$ 个,且第 $i$ 时刻选的元素个数不能超过 $a_{i}$.         

显然,对于贡献为负数的肯定要全选,然后贡献为正的话先选上,然后再维护一个堆来删除即可.       

code:   

#include <queue> 
#include <cstdio> 
#include <algorithm>    
#include <cstring>   
#define N 1000009  
#define ll long long 
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std; 
ll sum[N];  
int a[N],w[N];
struct data {  
    ll v;
    int k;  
    data(ll v=0,int k=0):v(v),k(k){}  
    bool operator<(const data b) const {  
        return v>b.v; 
    }
};       
priority_queue<data>q;    
int main() {  
    // setIO("input");              
    int n,k;   
    scanf("%d%d",&n,&k);  
    for(int i=1;i<=n;++i) scanf("%d",&a[i]);  
    for(int i=1;i<=n;++i) scanf("%d",&w[i]); 
    for(int i=n;i>=1;--i) sum[i]=sum[i+1]+1ll*w[i];     
    ll mx=0;  
    ll cur=0;  
    for(int i=1;i<=n;++i) {   
        if(sum[i]<=0) {                
            mx-=1ll*k*sum[i];                     
            cur-=1ll*k;  
        }
        else {   
            mx+=1ll*k*sum[i];  
            cur+=1ll*k;              
            q.push(data(sum[i],k<<1));  
        }    
        while(cur>a[i]) {       
            data e=q.top(); q.pop();   
            if(cur-a[i]>e.k) {           
                mx-=1ll*e.k*e.v;   
                cur-=e.k;        
            }       
            else {   
                int det=cur-a[i];    
                mx-=1ll*det*e.v;   
                cur-=det;  
                if(e.k==det) break;  
                else q.push(data(e.v,e.k-det));     
            }
        }        
    }   
    printf("%lld\n",mx);   
    return 0; 
}

  

posted @ 2020-07-05 08:05  EM-LGH  阅读(190)  评论(0编辑  收藏  举报