标签:col ora 策略 mes cal i+1 ios 十分 贪心
首先,$\mathcal O(n^2)$ 的思路十分好想。
大概就是说,我们发现答案序列是单调不降的,所以倒着来,对于每个位置 $i$,我们找一个它后面的位置 $j$,使得 $\frac{\sum_{k=i}^{j} a_k}{j-i+1}$ 是最小的,然后把这一段全部赋值为这个值。这样,我们就在优先确保前面的值更优时,得到了一个可靠的贪心策略。
因为 $10^6$ 肯定要更好的方法,所以考虑优化。当处理位置 $i$ 的时候,$(i, n]$ 已经处理好了,成为了一个不降的,若干个连续段组成的数列。而对于连续的一段,我们 要不一起选,要不一起不选,所以就可以用一个栈来维护后面的这个东西。栈里存一个三元组,表示这一段的左端点 $l$,右端点 $r$,以及总和 $w$。
比如这时,已选的数字的总和为 $sum$,已选的数字总个数为 $tot$,显然,如果处于栈顶的那一段(不妨叫做 $top$)能够 拖低平均值,即 $\frac{w_{top}}{r_{top}-l_{top} +1} \le \frac{sum}{tot}$(注意等于也可以不加,反正不会改变什么),那么我们就要选这一段。细节问题是避免浮点计算,交叉相乘,看看 $w_{top} \times tot \le sum \times (r_{top}-l_{top}+1)$ 就行了。
时间复杂度 $\mathcal O(n)$,下面是代码。
#include <bits/stdc++.h> using namespace std; const int N = 1e6 + 5; int n, l[N], r[N], top, a[N]; long long w[N]; double ans[N]; int main() { scanf("%d", &n); for(register int i = 1; i <= n; i++) scanf("%d", &a[i]); top = 1; l[1] = r[1] = n; w[1] = a[n]; l[0] = n + 1; for(register int i = n - 1; i; i--) { int tot = 1; long long sum = a[i]; while(top && w[top] * tot <= sum * (r[top] - l[top] + 1)) { sum += w[top]; tot += r[top] - l[top] + 1; top--; } top++; r[top] = l[top - 1] - 1; l[top] = i; w[top] = sum; } for(register int i = 1; i <= top; i++) for(register int j = l[i]; j <= r[i]; j++) ans[j] = 1.0 * w[i] / (r[i] - l[i] + 1); for(register int i = 1; i <= n; i++) printf("%.10lf\n", ans[i]); return 0; }
吐槽:
所以这是一道毒瘤题……
CodeForces 1299C Water Balance
标签:col ora 策略 mes cal i+1 ios 十分 贪心
原文地址:https://www.cnblogs.com/syksykCCC/p/CF1299C.html