【贪心】LuoguP5653 基础最优化练习题 (我想不到的贪心

P5653

题目大意:

一个x初值为0,每次可以给x加上值y,y∈[-k,k],同时必须保证第i次操作后 xi≤ai。需要进行n次操作。

有一个长度为n的序列 w,wi∈[-106,106]。

设第i次操作后x的值为bi,要求最大化Σni=1biwi。输出最大值。

数据范围:n106, 106wi106, 0ai108, 1k100。

做法:

y,w,a,b 所表示的 如题意。

Σni=1biwi = Σni=1biΣij=1yi = Σni=1yiΣnj=ibj

因为b是定值 所以 设cinj=ibj,即c是b的后缀和。

所以有:Σni=1biwi = Σni=1yici

设diij=1bj,即 d是y的前缀和。 

则 限制 是

  对于所有di≤ai,对于所有y∈[-k,k]。 

讲题人的题解是这么说的:

  此时我们可以考虑根据c 贪心。
  我们按照c 从大到小贪心,在能取的情况下尽量多取。
  这样子贪心的正确性可以通过网络流建模来证明。由于建模部分较 为复杂这里不再展开。

所以,问题转化为,对于后缀和c,以c最大为最优,使c在不超“限制”的情况下取得对应y最大。

对我来说,这个贪心很难想。

知道这样子最优,做法也很难想。

结果删删改改 ,没有多少代码。只是  想无 (:3_ヽ)_

#include<bits/stdc++.h>
#define debug printf("!");
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int maxn=1e6+50;

struct P{
    ll w,lim;
    bool operator <(const P&p)const{return w>p.w;}
}poi;
ll a[maxn],b[maxn];
priority_queue<P>que;

/*
 从1到n 使得当前值取当前最优, 超过限制的值, 去找前i个值里小于 最优性低于当前值的最不优的可修改的值 减
 无法修改的值就 吐出来
 无法修改的值也 不要放进去

 这样子 才能使 最大的尽可能最优
 然后 这道题的贪心做法 要的就是使最大的值最优

 */

int main()
{
    int n;
    ll k,ans=0,sum=0,t,d,tt;
    scanf("%d%lld",&n,&k);
    for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
    for(int i=1;i<=n;i++)scanf("%lld",&b[i]);
    for(int i=n-1;i>=1;i--)b[i]+=b[i+1];
    for(int i=1;i<=n;i++)
    {
        if(b[i]<=0)
        {
            ans-=b[i]*k;
            sum-=k;
            t=sum-a[i];
            if(t<=0)continue;
            d=0;//累积可以抵消的限制
            while((!que.empty())&&d<t)
            {
                poi=que.top();que.pop();
                tt=min(t-d,poi.lim);
                d+=tt;
                poi.lim-=tt;
                ans-=poi.w*tt;
                sum-=tt;
                if(poi.lim)
                {
                    que.push(poi);break;
                }
            }
            continue;
        }
        if(que.empty())
        {
            t=min(a[i]-sum,k);
            ans+=b[i]*t;
            sum+=t;
            que.push(P{b[i],t+k});
            continue;
        }
        ans+=b[i]*k;
        sum+=k;
        que.push(P{b[i],k+k});
        t=sum-a[i];//被限制
        if(t<=0)continue;
        d=0;//累积可以抵消的限制
        while((!que.empty())&&d<t)
        {
            poi=que.top();que.pop();
            tt=min(t-d,poi.lim);
            d+=tt;
            poi.lim-=tt;
            ans-=poi.w*tt;
            sum-=tt;
            if(poi.lim)
            {
                que.push(poi);break;
            }
        }
    }
    printf("%lld\n",ans);
}

猜你喜欢

转载自www.cnblogs.com/kkkek/p/11865180.html