CodeForces 639E Bear and Paradox
题目描述:
你现在在打一场比赛,一共有
N
N 道题,为了完成第
i
i 道题你需要连续花费
ti
ti 分钟的时间。
令
T=∑iti
T=∑iti ,那么在第
x
x 分钟结束时完成第
i
i 道题将会使你获得
pi⋅(1−c⋅xT)
pi⋅(1−c⋅xT) ,其中
c∈[0,1]
c∈[0,1] 是一个实数常量。在任何情况下,你都会采取最优的策略来做题,使得分数之和最大。对于一个
c
c ,可能存在多种不同的最优策略。
求最大的
c
c ,使得不存在这样一种最优策略:存在一对题目
(i,j)
(i,j) ,满足
pi<pj
pi<pj ,且第
i
i 题获得的分数严格大于第
j
j 题的。
题解:
观察式子发现,最优策略与
c
c 无关,且永远是按照
piti
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;
}