【Codeforces 639E】Bear and Paradox

贪心,二分答案。

题目大意:

有 $n$ 道题,每道题有一个初始得分 $p_i$ 和做这道题需要花费的时间 $t_i$。令 $T=\sum\limits_{i=1}^n t_i$。

还有一个实数 $c\in[0,1]$。

你需要安排做题的顺序,并按顺序做题。第 $i$ 道题的最终得分为 $p_i\times(1-\frac{c\cdot x}{T})$,$x$ 表示从一开始到你做完这道题花费的总时间。

要求保证,不存在一种最优策略,对于满足 $p_i\lt p_j$ 的 $i,j$,$i$ 题最终得分严格大于 $j$ 题最终得分。最优策略指总分最大的策略。

求能够满足条件的 $c$ 的最大值。

题解:

考虑一种做题的顺序,交换相邻两道题对答案产生的贡献的差值。假设交换的题目为 $p_1,p_2$,时间为 $t_1,t_2$,之前的题的时间总和为 $t_0$。

所以最优策略满足 $\frac{p_i}{t_i}$ 单调不上升。否则一定可以通过交换相邻两个使答案变大。

对于 $\frac{p_i}{t_i}$ 相同的题目,可以任意交换顺序,不会影响答案。我们可以对于每道题,求出它最早完成的时间和最晚完成的时间(分别放在 $\frac{p_i}{t_i}$ 相同的题的最前面和最后面)。

由于 $c$ 满足单调性,所以考虑二分答案。

我们要求判断的是,对于 $p_i \lt p_j$ 是否能让 $j$ 题比 $i$ 题得分低。

我们按照 $p_i$ 从小到大排序。

对每个题 $i$,我们要尽可能让它的得分小(使用最晚完成时间计算),并和前面的题比较。而对于前面的题,我们要尽可能让它的得分大(使用最早完成时间计算)。如果前面题的最大得分超过当前题的最小得分,则这个 $c$ 不可行。

我们不需要每次都暴力对每个判断,只需要记录前缀最大得分即可。

时间复杂度 $O(n\log n)$。

Code:

#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long LL;
struct prob{
    int t,p;LL Ft,Lt;
    inline bool operator<(const prob&rhs)const{return(LL)p*rhs.t>(LL)rhs.p*t;}
}a[150005];
inline bool cmp(const prob&a,const prob&b){return a.p<b.p;}
int n;
LL T;
bool check(double c){
    double mx=0;
    int lst=1;
    for(int i=2;i<=n+1;++i)
    if(a[i].p!=a[lst].p){
        for(int j=lst;j<i;++j)
        if(mx>a[j].p*(1-c*a[j].Lt/T))return 0;
        for(int j=lst;j<i;++j)
        mx=max(mx,a[j].p*(1-c*a[j].Ft/T));
        lst=i;
    }
    return 1;
}
void init(){
    sort(a+1,a+n+1);
    LL ft=0,lt=0;
    for(int i=1;i<=n;++i)
    if(i==1||a[i-1]<a[i]){
        ft=lt;
        lt+=a[i].t;
        for(int j=i+1;j<=n&&!(a[j-1]<a[j]);++j)lt+=a[j].t;
        a[i].Ft=ft+a[i].t,a[i].Lt=lt;
        for(int j=i+1;j<=n&&!(a[j-1]<a[j]);++j)
        a[j].Ft=ft+a[j].t,a[j].Lt=lt;
    }
    sort(a+1,a+n+1,cmp);
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;++i)scanf("%d",&a[i].p);
    for(int i=1;i<=n;++i)scanf("%d",&a[i].t),T+=a[i].t;
    init();
    double l=0,r=1;
    for(int i=1;i<=40;++i){
        const double mid=(l+r)/2;
        if(check(mid))l=mid;else r=mid;
    }
    printf("%.9g\n",l);
    return 0;
}