[题解] [JSOI2014] 宅男计划

题面

题解

首先我们发现, 那些保质期短, 价格又贵的我们肯定不会选

所以先拿单调队列弹掉这些

然后就在保质期内选最便宜的就行

这样我们发现, 外卖小哥来的次数少, 我们每次买的就多, 花费就多, 外卖小哥来的次数多, 我们每次买的就少, 但是付外卖的钱又多了起来

经过看题解后发现, 这应该是一个单峰函数

那么我们三分一下

最优情况下应该是保质期内买最便宜的, 并且每次外卖小哥来送的货都是一样的(除了最后一次不一定)

那么只要我们买不满, 就把他丢到最后一次送就行

Code

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
typedef long long ll;
const int N = 205; 
using namespace std;

int n, top;
ll m, K, ans;
struct node
{
    ll p, t;
    bool operator < (const node &p) const
	{
	    return t < p.t; 
	}
} a[205], stk[205]; 

template < typename T >
inline T read()
{
    T x = 0, w = 1; char c = getchar();
    while(c < '0' || c > '9') { if(c == '-') w = -1; c = getchar(); }
    while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x * w; 
}

ll f(ll x)
{
    ll rest = m - K * x, d = 0, len = 0;
    for(int i = 1; i <= n; i++)
    {
	ll tmp = min(a[i].t - len, rest / (a[i].p * x));
	len += tmp, d += tmp * x, rest -= tmp * a[i].p * x; 
	if(len < a[i].t) { d += rest / a[i].p; break; }
    }
    return d; 
}

int main()
{
    m = read <ll> (), K = read <ll> (), n = read <int> ();
    for(int i = 1; i <= n; i++) a[i].p = read <ll> (), a[i].t = read <ll> () + 1;
    sort(a + 1, a + n + 1);
    for(int i = 1; i <= n; i++)
    {
	while(top && stk[top].p >= a[i].p) top--;
	stk[++top] = a[i];
    }
    n = top;
    for(int i = 1; i <= n; i++) a[i] = stk[i];
    ll l = 1, r = m / K;
    while(r - l >= 5)
    {
	ll lmid = (2 * l + r) / 3, rmid = (2 * r + l) / 3;
	if(f(lmid) < f(rmid)) l = lmid + 1;
	else r = rmid - 1; 
    }
    for(ll i = l; i <= r; i++) ans = max(ans, f(i));
    printf("%lld\n", ans); 
    return 0; 
}
posted @ 2020-02-11 20:12  ztlztl  阅读(259)  评论(0编辑  收藏  举报