题目
有n道题目,第i道题目有pi的分值,完成第i道题目需要ti的时间。令总时间T=∑ti,第i道题目的得分为pi×(1−cxT),其中c∈[0,1]为一个参数,x为完成这道题后的累计用时(加上完成之前题目的用时),显然至少存在一种完成顺序使得总得分最大.
如果确定c后,在一种使得总得分最大的完成顺序中,存在pi<pj但是i的得分严格大于j的得分,则c是一个不合法的参数。你要找到最大的合法的c.
思路
最佳做题顺序一定是按照
p
i
t
i
\frac{p_i}{t_i}
tipi 从大到小排序的,在所有
p
i
t
i
\frac{p_i}{t_i}
tipi 都不相同的情况下,设排序后第
i
i
i 个问题的得分为
p
i
−
p
i
∑
j
=
1
i
t
j
T
c
p_i - \frac{p_i\sum_{j=1}^{i}t_j}{T}c
pi−Tpi∑j=1itjc,即有
n
n
n 个关于
c
c
c 的一次函数。
题目要求不存在悖论,等价于最大的
c
c
c 满足这
n
n
n 个一次函数在
(
0
,
c
)
(0,c)
(0,c) 中不存在交点。
找到对于每个相同的
p
i
p_i
pi 最靠上和最靠下的一次函数,解出相邻两个
p
i
p_i
pi 对应的函数交点,取最小的横坐标即可。
当有
p
i
t
i
\frac{p_i}{t_i}
tipi 相同的时候,会出现有多个最佳做题顺序,因此需要找到每道题对应的一次函数的斜率的最大和最小值。
代码
#include<bits/stdc++.h>
#define db double
using namespace std;
const int N=2e5+77;
const db eps=1e-8;
int n;
db l,r,mid,sm[N],mxp[N],mnp[N];
struct A
{
db p,t,c;
int id;
}a[N],b[N];
int cmp(A x,A y)
{
return x.c>y.c;
}
int cmp0(A x,A y)
{
return x.p<y.p;
}
int check()
{
db mxv=0;
for(int i=1,j,lb,rb; i<=n; i=j)
{
j=i;
while(j<=n&&a[j].c==a[i].c)j++;
lb=i,rb=j-1;
for(int k=i;k<j;++k)
{
mnp[a[k].id]=a[k].p*(1-mid*sm[rb]/sm[n]);
mxp[a[k].id]=a[k].p*(1-mid*(sm[lb-1]+a[k].t)/sm[n]);
}
}
for(int i=1,j; i<=n; i=j)
{
j=i;
while(j<=n&&b[j].p==b[i].p)j++;
for(int k=i;k<j;++k)if(mxv>mnp[b[k].id])return 0;
for(int k=i;k<j;++k)mxv=max(mxv,mxp[b[k].id]);
}
return 1;
}
int main()
{
scanf("%d",&n);
for(int i=1; i<=n; i++) scanf("%lf",&a[i].p),a[i].id=i;
for(int i=1; i<=n; i++) scanf("%lf",&a[i].t),a[i].c=a[i].p/a[i].t;
memcpy(b,a,sizeof(a));
sort(b+1,b+n+1,cmp0);
sort(a+1,a+n+1,cmp);
for(int i=1; i<=n; i++) sm[i]=sm[i-1]+a[i].t;
l=0,r=1;
while(r-l>eps)
{
mid=(l+r)/2;
if(check())l=mid;
else r=mid;
}
printf("%.7lf\n",l);
}