CodeForces 639E Bear and Paradox
题目描述:
你现在在打一场比赛,一共有
N
N 道题,为了完成第
i
i 道题你需要连续花费
t i
ti 分钟的时间。
令
T = ∑ i t i
T=∑iti ,那么在第
x
x 分钟结束时完成第
i
i 道题将会使你获得
p i ⋅ ( 1 − c ⋅ x T )
pi⋅(1−c⋅xT) ,其中
c ∈ [ 0 , 1 ]
c∈[0,1] 是一个实数常量。在任何情况下,你都会采取最优的策略来做题,使得分数之和最大。对于一个
c
c ,可能存在多种不同的最优策略。
求最大的
c
c ,使得不存在这样一种最优策略:存在一对题目
( i , j )
(i,j) ,满足
p i < p j
pi<pj ,且第
i
i 题获得的分数严格大于第
j
j 题的。
题解:
观察式子发现,最优策略与
c
c 无关,且永远是按照
p i t i
piti 递增的顺序做题。于是,对于每一道题,我们可以处理出它的最大和最小可能完成时间是多少。
之后二分答案
c
c ,如果有一道题,它的最小可能得分(这个可以直接利用我们预处理的东西算出来)严格小于之前
p
p 值比它小的题目的最大可能得分(也可以算出来),那么这个
c
c 值就是不可行的。
具体实现细节参见代码。
题目链接: vjudge 原网站
代码:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
using namespace std ;
#define MAXN 150010
#define EPS (1e-8)
#define INF 1e10
static struct tProblem
{
long long p, t, sum_t, max_t, min_t;
} val[MAXN];
static int N;
static long long T;
inline bool cmp1(const tProblem x, const tProblem y)
{
return x.p * y.t > y.p * x.t;
}
inline bool cmp2(const tProblem x, const tProblem y)
{
return x.p < y.p;
}
int main()
{
scanf ("%d" , &N);
for (int i = 1 ; i <= N; i++) scanf ("%lld" , &val[i].p);
for (int i = 1 ; i <= N; i++) scanf ("%lld" , &val[i].t), T += val[i].t;
sort(val + 1 , val + N + 1 , cmp1);
for (int i = 1 ; i <= N; i++) val[i].sum_t = val[i-1 ].sum_t + val[i].t;
for (int i = 1 , j; i <= N; i = j)
{
for (j = i; j <= N && val[i].p * val[j].t == val[j].p * val[i].t; j++);
for (int k = i; k < j; k++)
val[k].min_t = val[i-1 ].sum_t + val[k].t, val[k].max_t = val[j-1 ].sum_t;
}
sort(val + 1 , val + N + 1 , cmp2);
double l = 0.0 , r = 1.0 ;
while (r - l > EPS)
{
double mid = (l + r) / 2 , mx = -INF, used_mx = -INF;
int flg = 1 ;
for (int i = 1 ; flg && i <= N; i++)
{
if (val[i].p != val[i-1 ].p) used_mx = mx;
if ((1.0 - mid * val[i].max_t / T) * val[i].p < used_mx)
flg = 0 ;
mx = max(mx, (1.0 - mid * val[i].min_t / T) * val[i].p);
}
if (flg) l = mid; else r = mid;
}
printf ("%.10lf\n" , l);
return 0 ;
}