Luogu4040 AHOI/JSOI2014 宅男计划 贪心、二分、三分

传送门


仍然对“为什么这个函数单峰”的问题毫无理解

首先,对于保质期又低、价格又贵的食物,我们显然不需要购买它。所以如果设\(pri_i\)表示保质期不小于\(i\)的所有食品中价格最低的食品的价格,那么\(pri\)数组显然单调不降。

考虑如果我们要直接去做比较麻烦,可是如果我们知道点外卖的次数,就很好计算了。

假设我们知道了我们总共要买\(x\)次外卖,不难知道这\(x\)次外卖点的食物能够坚持的时间的极差不会超过\(1\),否则可以用多的补少的,在不降低总坚持时间的情况下降低总价格(因为\(pri\)单调不降)。

假设\(x\)次外卖坚持的时间为\(K\)\(K+1\),那么可以先二分出\(K\),然后将剩下的钱尽可能多的买\(pri_{K+1}\)

现在,确定点外卖的次数就可以求得对应的能够坚持的时间,问题就是如何求最长的时间。不难发现点外卖的次数与坚持的时间对应的函数是一个单峰函数,直接上三分就完事了。

有没有神仙会证这个函数单峰啊QAQ

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<ctime>
#include<cctype>
#include<algorithm>
#include<cstring>
#include<iomanip>
#include<queue>
#include<map>
#include<set>
#include<bitset>
#include<stack>
#include<vector>
#include<cmath>
#define INF 0x3f3f3f3f
#define int long long
#define PII pair < int , int > 
#define st first
#define nd second
//This code is written by Itst
using namespace std;

inline int read(){
    int a = 0;
    char c = getchar();
    bool f = 0;
    while(!isdigit(c) && c != EOF){
        if(c == '-')
            f = 1;
        c = getchar();
    }
    if(c == EOF)
        exit(0);
    while(isdigit(c)){
        a = (a << 3) + (a << 1) + (c ^ '0');
        c = getchar();
    }
    return f ? -a : a;
}

vector < PII > tg;
vector < PII > :: iterator it , it1;
int M , F , N , sum[210];
bool f[210];

inline int calcSum(int dir){
    it = lower_bound(tg.begin() , tg.end() , PII(dir , -1));
    int t = it - tg.begin();
    if(t && !f[t - 1] || (t ? sum[t - 1] : 0) + 1.0 * tg[t].nd * (dir - (t ? tg[t - 1].st : -1)) > M)
        return 2e18;
    return (t ? sum[t - 1] : 0) + tg[t].nd * (dir - (t ? tg[t - 1].st : -1));
}

inline int calc(int x){
    int s = M - x * F , L = -1 , R = tg[N - 1].st;
    while(L < R){
        int mid = L + R + 1 >> 1;
        calcSum(mid) <= s / x ? L = mid : R = mid - 1;
    }
    s -= calcSum(L) * x;
    if(L == tg[N - 1].st)
        return (L + 1) * x;
    it = lower_bound(tg.begin() , tg.end() , PII(L + 1 , -1));
    return (L + 1) * x + s / it->second;
}

inline bool check(int mid){
    return calc(mid) >= calc(mid + 1);
}

signed main(){
#ifndef ONLINE_JUDGE
    //freopen("in" , "r" , stdin);
    //freopen("out" , "w" , stdout);
#endif
    M = read();
    F = read();
    N = read();
    for(int i = 1 ; i <= N ; ++i){
    	int a = read() , b = read();
    	tg.push_back(PII(b , a));
    }
    sort(tg.begin() , tg.end());
    for(it = --tg.end() , it1 = it , --it1 ; it != tg.begin() ; --it , it1 = it , --it1)
        if(it->nd < it1->nd)
            it1->nd = it->nd;
    for(it = tg.begin() , it1 = it , ++it1 ; it1 != tg.end() ; ++it , it1 = it , ++it1)
        if(it->st == it1->st)
            it1->nd = it->nd;
    for(int i = 0 ; i < N ; ++i)
        if((i == 0 ? 0 : sum[i - 1]) + 1.0 * tg[i].nd * (tg[i].st - (i == 0 ? -1 : tg[i - 1].st)) <= M){
            f[i] = 1;
            sum[i] = (!i ? 0 : sum[i - 1]) + tg[i].nd * (tg[i].st - (!i ? -1 : tg[i - 1].st));
        }
        else
            break;
    int L = 1 , R = M / F;
    while(L < R){
    	int mid = (L + R) >> 1;
    	check(mid) ? R = mid : L = mid + 1;
    }
    cout << calc(L);
    return 0;
}
posted @ 2019-01-28 20:39  cjoier_Itst  阅读(180)  评论(0编辑  收藏  举报