CF639E Bear and Paradox 题解

题面(带翻译)戳这里
算法:贪心
二分\(c\),考虑如何check
首先,利用邻项交换的方法,可以证明最终的方案一定是按\(\frac{p_i}{t_i}\)排序的序列
简单证明如下
\(p_i\)\(x+t_i\)时刻完成,\(p_{i+1}\)\(x+t_i+t_{i+1}\)时刻完成
\(p_i\)\(p_{i+1}\)前面的充要条件是\(p_i(1-\frac{c(x+t_i)}{T})+p_{i+1}(1-\frac{c(x+t_i+t_{i+1})}{T})-p_{i+1}(1-\frac{c(x+t_{i+1})}{T})-p_{i}(1-\frac{c(x+t_i+t_{i+1})}{T})>0\)
化简一下就可以得到上面的式子
这可以在二分之前先排序
由于是所有情况都要满足,我们只要分析最坏的情况即可
对于每个问题,最早完成时间就是它在这一"段"中最早执行的时间,最晚完成时间就是它在这一"段"中最晚完成的时间
这里"段"指的是\(\frac{p_i}{t_i}\)相同的连续的一段问题组成的集合
那么能完成问题的条件就是对于任意\(p_i>p_j\),\(i\)在最晚完成时间的分数严格大于\(j\)在最早时间完成的分数
排序可以在二分之前先排

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
double eps = 1e-8;
const int N = 200005;
double ear[N],sum[N],lat[N];
double minn[N];
int n,pre[N];
inline int read() {
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9') {
        if(ch=='-')
            f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9') {
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    return x*f;
}
struct pro{
	int p,t,id;
	double now;
}a[N];
bool cmp(pro u,pro v) {
	return u.now > v.now;
}
bool cmp2(pro u,pro v) {
	return u.p > v.p;
}
bool check(double mid) {
	for(int i = 1;i <= n;i++) {
		double nw = a[i].p*(1-ear[a[i].id]*mid/sum[n]);
		if(nw > minn[pre[i]]) return false;
		minn[i] = min(minn[i-1],a[i].p*(1-lat[a[i].id]*mid/sum[n]));
	}
	return true;
}
int main () {
	minn[0] = 0x3f3f3f3f;
	n = read();
	for(int i = 1;i <= n;i++) {
		a[i].p = read();
	}
	for(int i = 1;i <= n;i++) {
		a[i].t = read();
		a[i].now = 1.0*a[i].p/a[i].t;
	}
	sort(a+1,a+n+1,cmp);
	for(int i = 1;i <= n;i++) {
		sum[i] = sum[i-1] + a[i].t;
		a[i].id = i;
	}
	for(int i = 1;i <= n;i++) {
		for(int j = i;j <= n+1;j++) {
			if(a[j].now != a[i].now||j > n) {
				for(int k = i;k < j;k++) {
					ear[k] = sum[i-1] + a[k].t;
					lat[k] = sum[j-1];
				}
				i = j-1;
				break;
			}
		}
	}
	sort(a+1,a+n+1,cmp2); 
	for(int i = 1;i <= n;i++) {
		for(int j = i;j <= n+1;j++) {
			if(a[j].p != a[i].p||j > n) {
				for(int k = i;k < j;k++) {
					pre[k] = i-1;
				}
				i = j-1;
				break;
			}
		}
	}
	double l = 0,r = 1;
	while(r - l > eps) {
		double mid = (l + r) / 2;
		if(check(mid)) {
			l = mid;
		}
		else r = mid;
	}
	printf("%.6lf\n",l);
	return 0;
}
posted @ 2020-12-15 10:19  ctt2006  阅读(53)  评论(0)    收藏  举报