飙车race
【题目描述】
最近,Lucas 参加了一个飙车比赛。比赛在环型赛道上进行,全程共 K 圈。在比赛中,选手需要用主办方提供的赛车,而这种老爷车给 Lucas 带来了巨大的麻烦。这种赛车的油箱可以装 n 个单位的油。每个单位的油可以支持赛车跑恰好 1 圈。每圈开始前,你需要保证你的油箱里的油量是一个正整数。一圈跑完后,油箱里的油会减少恰好 1 个单位。每一开始前,你可以进入加油站进行加油,一次加油的耗时是一个固定的常数 P。每次加油时你可以把油量加至任意你想要的整数。特别地,如果开始一圈时剩余油量恰好为 1,这也是允许的:结束这一圈时剩余油量为 0,此时你必须进站加油,除非你恰好跑完最后一圈。比赛开始前,你也可以进行加油,这次加油不会被计入比赛时间。赛车在不同油量下的速度是不同的。经过测试,如果一圈开始时油箱中剩余的油量为 i 单位,则跑完这圈需要 ti单位时间。当然,符合常识的是,油箱里的油越多,车就跑得越慢,所以我们保证 ti <= ti+1 。作为一名老司机,Lucas 自然会参加很多场比赛。他共参加了 Q 场比赛,每场比赛
都有一个独立的 K 和 P。请你帮 Lucas 的粉丝 Yazid 求出每场比赛 Lucas 的最短比赛用时。
由于题意难以描述,在此只好附上题目...
第一行两个正整数n,Q表示油箱大小和比赛场数
n个正整数t1,t2...tn表示油箱大小为1至n时,赛车跑一圈要用的时间。
接下来Q行,每行两个正整数K,P,描述这场比赛。
哎一言难尽啊,作为T3,部分分怎么能给的这么少?!蒟蒻表示少得可怜啊...然后就打炸了。
30分应该可以用DP乱搞,其他还有10分P=1的部分分。
60分用n^2的贪心,100分就要用到三分法加贪心啦...真心不容易qwq。
先讲n^2贪心,100分只要加一个三分即可。
n^2的算法,显而易见,如果一个决策是最优的,那么每次加油都应该加这个决策的油量,如果还有剩余就平均分配到前面的几次中,每次多加一个单位的油,这个贪心想到后面就很简单辣。
枚举每一次加的油量,就能O(1)的时间内算出需要花费的时间。总时间复杂度O(Qn)就能拿到60分。
至于100分,应该可以发现这样决策的函数是单峰的,简单的想一下,太左边不是最优的,太右边也不是最优的,具体证明其实我也只知道一点点qwq...那就不证了,可这竟然是我打的第一道三分,真是道好题真的。
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#define ll long long
#define N 200000+2000
using namespace std;
int n,Q;
ll s[N],t[N],ans1,ans2,ans,mid1,mid2,K,P;
ll calc(int x){
ll sum=K/x;
ll sum2=K-sum*x;
ll sum1=x-sum2;
return sum1*s[K/x]+sum2*s[K/x+1]+P*(x-1);
}
ll read(){
ll num=0;char ch=getchar();
while (ch>='0' && ch<='9') num=num*10+ch-'0',ch=getchar();
return num;
}
int main()
{
freopen("race.in","r",stdin);
freopen("race.out","w",stdout);
n=read();Q=read();
for(int i=1;i<=n;i++) t[i]=read();
for(int i=1;i<=n;i++) s[i]=s[i-1]+t[i];
while(Q--){
K=read(),P=read();
ans=1e9*1e9;
ll l=(K-1)/n+1,r=K;
while(l<=r){
mid1=(l+r)>>1;mid2=mid1+1;
if(mid2>r) mid2=r;
ans1=calc(mid1);ans2=calc(mid2);
if(ans1>ans2) l=mid1+1;
else if(ans1<ans2) r=mid2-1;
else{
l=mid1+1;r=mid2-1;
ans=ans1;
}
}
cout<<ans<<endl;
}
return 0;
}