【洛谷P5653】基础最优化练习题
贪心
题目大意:
给定数列 $a_1,a_2,\dots,a_n$,和 $w_1,w_2,\dots,w_n$,还有 $b_0,b_1,b_2,\dots,b_n$,初始 $b_0=0$。
要求构造出一个数列 $b$,满足 $b_i-b_{i-1}\in[-k,k]$ 且 $b_i\leq a_i$。并最大化 $\sum_{i=1}^n b_i\times w_i$.
求这个的最大值。
题解:
考虑 $b_i$ 与 $b_{i-1}$ 的差值 $\Delta$ 会对 $w_i\sim w_n$ 都产生影响。
即令 $\Delta b_i=b_i-b_{i-1},f_i=\sum_{j=i}^n w_j$.
那么要求最大化的就是 $\sum_{i=1}^n \Delta b_i\times f_i$,需要满足 $\Delta b_i\in[-k,k]$ 且 $\sum_{j=1}^i \Delta b_j\leq a_i$.
先不考虑 $a$ 的限制,那么我们显然对每个大于 $0$ 的 $f_i$,令 $\Delta b_i=k$,其余的 $\Delta b_i=-k$。
考虑存在 $a$ 的限制。我们从小到大依次加入 $\Delta b_i$,即当前的 $b_i$。如果超过了 $a_i$,那么我们就需要通过减少之前的 $\Delta b_i$ 来满足条件。我们显然是贪心地减少一个 $f_{i’}$ 最小的位置 $i’$ 的值。
这个可以使用堆维护,以 $f_i$ 为关键字。由于考虑到当前 $i$ 时,前 $i-1$ 个位置已经满足条件,因此减少之前的并不会产生影响。
时间复杂度 $O(n\log n)$。
Code:
#include<iostream>
#include<queue>
using namespace std;
const int N=1e6+6;
typedef long long LL;
int n,k;
LL f[N],ans;
int t[N],a[N];
struct data{
LL d;int id;
inline bool operator<(const data&rhs)const{return d>rhs.d;}
};
priority_queue<data>q;
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>k;
for(int i=1;i<=n;++i)cin>>a[i];
for(int i=1;i<=n;++i)cin>>f[i];
for(int i=n-1;i;--i)f[i]+=f[i+1];
int suf=0;
for(int i=1;i<=n;++i){
t[i]=f[i]>0?k:-k;
suf+=t[i];
ans+=f[i]*t[i];
if(t[i]==k)q.push((data){f[i],i});
while(suf>a[i]){
int id=q.top().id;q.pop();
const int dlt=suf-a[i];
if(dlt<=t[id]+k)t[id]-=dlt,ans-=dlt*f[id],suf=a[i];
else suf-=t[id]+k,ans-=(t[id]+k)*f[id],t[id]=-k;
if(t[id]>-k)q.push((data){f[id],id});
}
}
cout<<ans<<'\n';
return 0;
}