[题解] [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;
}