自己可以想出来个七七八八,但很多地方没有把细节处理好,思考问题不全面,然后就花了很长时间……
显然答案具有单调性,直接二分答案。
对于一个二分的答案 \(c\) ,思考如何找到最优的做题顺序,考虑相邻的两道题,把他们的顺序调换,看最终的得分会如何变化。因为把这两道题调换,不会影响其他题的得分,所以得分的变化值是很好算的。一通分析之后,我们发现把 \(\frac{p_i}{t_i}\) 大的排在前面,可以使得答案最优。
到这里似乎就大功告成了,只需对于每个 \(c\) 都按这个顺序算一遍,然后看看内部有没有 paradox
就好了。但是这个题折磨的地方才刚刚开始。
按这个思路写一遍,一测第二个样例,欸,你猜怎么着,错了!哈哈哈
原来没有考虑 \(\frac{p_i}{t_i}\) 相同的情况,因为如果 \(\frac{p_i}{t_i}\) 相同,这些题在顺序中必然是连续的一段,但同时他们也可以随意互相调换。每一种排列方案对 \(c\) 的承受度是不一样的。
然后我的智熄操作就开始了。我就想,啊对对对,然后就额外单独处理每类 \(\frac{p_i}{t_i}\) 相同的题目,因为顺序可以随便调换,所以一个题如果要尽量得分高,就把他丢最前面,否则丢最后面,然后看看有无 paradox
即可。
然后一顿乱调,然后又 wa
了。
然后发现 \(\frac{p_i}{t_i}\) 不同的题目也可以互相影响,实际上现在每一个题目都有一个 \(\min\) 和一个 \(\max\),这取决于他们能被放置的时间段。到这里基本上就无懈可击了。但由于之前的种种遗留,复杂度被莫名其妙堆到了 \(O(n\log^2_2 n)\),理论上是能过的,但是浮点数运算常数巨大,然后就挂了。后面找了很久才发现可以到 \(O(n\log_2 n)\) 。但又有一个很致命的点,就是精度问题。我的精度一直设的是 eps = 1e-7
,但其实这只是满足了答案要求的精度,在运算过程中一些地方理论上需要 eps = 1e-14
的精度(就看最小能到多少!!),然而虽然实际上我开 eps = 1e-12
也能过,但这个精度问题实在是把我搞傻了。绷不住了。
最后反正就是过了。虽然这个题看起来很蠢,但要考虑的细节还是有点多,不过主要是因为我想问题不够全面,总是看到一个问题,就直接改,属于是过拟合了。还是要多想啊。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <ctime>
using namespace std;
const int N = 200005;
const double eps = 1e-12;
const double INF = 1e9;
struct Dt {
double p, t, tl, tr, w;
}s[N];
bool cmp(Dt x, Dt y) {
return x.w > y.w;
}
bool cmp1(Dt x, Dt y) {
return x.p < y.p;
}
int n = 0, cnt = 0;
double T = 0;
bool vis[N];
bool check(double c) {
int u = 0;
double Mx = -INF;
for (int i = 1; i <= n; i++) {
while (u + 1 < i && abs(s[u + 1].p - s[i].p) > eps) {
u++;
Mx = max(Mx, s[u].p * (1 - c * (s[u].tl + s[u].t) / T));
}
double w = s[i].p * (1 - c * s[i].tr / T);
if (abs(Mx - w) > eps && Mx > w)
return false;
}
return true;
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%lf", &s[i].p);
for (int i = 1; i <= n; i++) {
scanf("%lf", &s[i].t);
T += s[i].t;
s[i].w = s[i].p / s[i].t;
}
sort(s + 1, s + n + 1, cmp);
int u = 0;
double T0 = 0;
for (int i = 1; i <= n; i++)
vis[i] = false;
for (int i = 1; i <= n; i++) {
while (u + 1 < i && abs(s[u + 1].p / s[u + 1].t - s[i].p / s[i].t) > eps) {
u++;
T0 += s[u].t;
}
if (i == n || abs(s[i].p / s[i].t - s[i + 1].p / s[i + 1].t) > eps) {
double T1 = T0;
for (int j = u + 1; j <= i; j++)
T1 += s[j].t;
for (int j = u + 1; j <= i; j++)
s[j].tl = T0, s[j].tr = T1;
}
}
sort(s + 1, s + n + 1, cmp1);
int cnt = 0;
double l = 0, r = 1;
while (l + eps < r) {
double mid = (l + r) / 2;
if (check(mid)) l = mid;
else r = mid;
}
printf("%.7f\n", l);
return 0;
}
标签:Paradox,int,double,eps,Bear,Dt,include,Mx,CF639E
From: https://www.cnblogs.com/kirakiraa/p/18083792