飙车

时间:2022-11-22 18:54:24


飙车(race)

1s/512MB

【题目背景】

老司机 Lucas Skipper 喜欢飙车。

【题目描述】

最近,Lucas 参加了一个飙车比赛。比赛在环型赛道上进行,全程共 K 圈。在比赛中,选手需要用主办方提供的赛车,而这种老爷车给Lucas 带来了巨大的麻烦。

这种赛车的油箱可以装 n 个单位的油。每个单位的油可以支持赛车跑恰好 1 圈。每圈开始前,你需要保证你的油箱里的油量是一个正整数。一圈跑完后,油箱里的油会减  少恰好 1 个单位。

每一圈开始前,你可以进入加油站进行加油,一次加油的耗时是一个固定的常数P。每次加油时你可以把油量加至任意你想要的整数。

特别地,如果开始一圈时剩余油量恰好为 1,这也是允许的:结束这一圈时剩余油量为 0,此时你必须进站加油,除非你恰好跑完最后一圈。

比赛开始前,你也可以进行加油,这次加油不会被计入比赛时间。

赛车在不同油量下的速度是不同的。经过测试,如果一圈开始时油箱中剩余的油量  为 i 单位,则跑完这圈需要 ti 单位时间。当然,符合常识的是,油箱里的油越多,车就跑得越慢,所以我们保证 ti ti+1。

作为一名老司机,Lucas 自然会参加很多场比赛。他共参加了 Q 场比赛,每场比赛

都有一个独立的 K P。请你帮 Lucas 的粉丝 Yazid求出每场比赛 Lucas 的最短比赛用时。

【输入格式】

从文件 race.in 中读入数据。

第一行 2 个正整数 n, Q,分别描述油箱大小和比赛场数。

接下来一行 n 个用空格隔开的正整数 t1 .. . tn,依次表示油箱余量为1 至 n 时,赛车跑一圈所需要的时间。

接下来 Q 行,每行包含 2 个正整数 K, P,描述一场比赛。

【输出格式】

输出到文件 race.out 中。

对于每场比赛,输出一行一个整数,表示完成比赛的最短用时。


【样例 1 输入】

2 3

10 100

3 4

1 1000

3 233

【样例 1 输出】

38

10

353

【样例 2】

见选手目录下的 race/race2.in race/race2.ans

【提示】

样例 1 解释:

对于询问 1,可以在每圈开始前加油 1 单位。于是加油耗时为 2 × 4 单位时间,跑

圈耗时为 10 + 10 + 10 单位时间。总耗时为 38 单位时间。

对于询问 2,可以在开局前加油 1 单位,由于开局前加油时间不计入总时间,因此总耗时为跑圈所需的 10 单位时间。

对于询问 3,可以在开局前加油 1 单位,在第 2 圈开始前加油 2 单位。于是加油耗

时为 1 × 233 单位时间,跑圈耗时为10 + 100+ 10 单位时间。总耗时为 353 单位时间。

【子任务】

对于 10% 的数据,保证 n ≤ 7,Q ≤ 100,K ≤ 7。对于 30% 的数据,保证 n, Q ≤100,K ≤ 100。

对于另外 10% 的数据,保证对于所有比赛,都有P = 1。

对于60% 的数据,保证 n, Q ≤ 1, 000。对于另外 20% 的数据,保证ti = i

对于 100% 的数据,保证 n, Q ≤ 200, 000,1 ≤ K, P, Ti ≤ 109

题解:这题有一定的贪心思想,若x为每加一次油跑几圈,f(x)为ans,则可以发现f(x)是一条类似抛物线的曲线。所以我们需要三分x求解。

Code:

var
k,i,mid1,mid2,n,q,l,r:longint;ans1,ans2,ans,p:int64;
a,b:array[0..200005] of int64;
function calc(x:longint):int64;
var s1,s2:int64;
begin
s1:=k div x;
s2:=k mod x;
exit(x*b[k div x]+s2*a[k div x+1]+p*(x-1));
end;
begin
assign(input,'race.in');reset(input);
assign(output,'race.out');rewrite(output);
readln(n,q);
for i:=1 to n do read(a[i]);
for i:=1 to n do b[i]:=b[i-1]+a[i];
while q>0 do
begin
dec(q);
readln(k,p);
l:=(k-1) div n+1;r:=k;
while (l<=r) do
begin
mid1:=(l+r) div 2;mid2:=mid1+1;
if mid2>r then mid2:=r;
ans1:=calc(mid1);ans2:=calc(mid2);
if ans1>ans2 then l:=mid1+1 else
if ans1<ans2 then r:=mid2-1 else
begin
l:=mid1+1;r:=mid2-1;
ans:=ans1;
end;
end;
writeln(ans);
end;
close(input);close(output);
end.