飙车

老司机 Lucas Skipper 喜欢飙车。最近,Lucas 参加了一个飙车比赛。比赛在环型赛道上进行,全程共 K 圈。在比赛中,选手需要用主办方提供的赛车,而这种老爷车给 Lucas 带来了巨大的麻烦。这种赛车的油箱可以装 n个单位的油。每个单位的油可以支持赛车跑恰好 1 圈。每圈开始前,你需要保证你的油箱里的油量是一个正整数。一圈跑完后,油箱里的油会减少恰好 1个单位。每一圈开始前,你可以进入加油站进行加油,一次加油的耗时是一个固定的常数 P。每次加油时你可以把油量加至任意你想要的整数。特别地,如果开始一圈时剩余油量恰好为 1,这也是允许的:结束这一圈时剩余油量为 0,此时你必须进站加油,除非你恰好跑完最后一圈。比赛开始前,你也可以进行加油,这次加油不会被计入比赛时间。赛车在不同油量下的速度是不同的。经过测试,如果一圈开始时油箱中剩余的油量为 i 单位,则跑完这圈需要 (t_i) 单位时间。当然,符合常识的是,油箱里的油越多,车就跑得越慢,所以我们保证 (t_ileq t_{i+1})。作为一名老司机,Lucas 自然会参加很多场比赛。他共参加了 Q 场比赛,每场比赛都有一个独立的 K 和 P。请你帮 Lucas 的粉丝 Yazid 求出每场比赛 Lucas 的最短比赛用时。

  据说这道题平均加油一定更优啊(我也不知道为什么),所以设x为一场比赛中加x次油,$g=lfloor frac{k}{x} floor $为每次加多少。

  所以(cost(x)=t_{g+1}(k\% x)+x(p+sum_{i=1}^gt_i)=xp+t_{g+1}(k\%x)+xsum_{i=1}^gt_i),可以发现这是个单峰函数,那么我们三分即可。

#include <cstdio>
using namespace std;
//#define debug

typedef long long LL;
const LL maxn=2e5+5;
LL k, p, n, q, t[maxn], sum[maxn];
inline LL min(LL x, LL y){ return x<y?x:y; }

LL calc(LL now){
    LL g=k/now;
    return t[g+1]*(k%now)+now*(p+sum[g]);
}

int main(){
#ifndef debug
    freopen("race.in", "r", stdin);
    freopen("race.out", "w", stdout);
#endif
    scanf("%lld%lld", &n, &q);
    for (LL i=1; i<=n; ++i)
        scanf("%lld", &t[i]), sum[i]=sum[i-1]+t[i];
    LL l, r, mid;
    for (; q; q--){
        scanf("%lld%lld", &k, &p);
        l=(k+n-1)/n, r=k;
        while (l<r-1){
            //这里r必须要等于mid+1,因为这是三分法!
            //并且还有两个元素的情况!所以while这么写!
            mid=(l+r)>>1;
            if (calc(mid)<calc(mid+1)) r=mid;
            else l=mid;
        }
        //开头加油不算时间!
        printf("%lld
", min(calc(l), calc(r))-p);
    }
#ifndef debug
    fclose(stdin);fclose(stdout);
#endif
    return 0;
}

原文地址:https://www.cnblogs.com/MyNameIsPc/p/7628754.html