题目

有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 piTpij=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);
}