传送门:http://codeforces.com/contest/939/problem/E
本题是一个序列上的问题。
一个序列S,初始时为空。依次给出Q个指令:
①将元素x加入S,保证x≥max(S);
②查询:max{max(T)-avg(T)|T⊆S}。
设在某一次查询时,S={ai|i=1,2,...,n},则a1≤a2≤...≤an。
定义$sum(k)=\sum_{i=1}^k a_i$,则ak=sum(k)-sum(k-1)。
设此时的查询值为ans。将x加入S,则对于所求的集合T,有以下两种情形:
①x不是T中的元素,则值为ans;
②x是T中的元素,则:max(T)=x;T中的其余元素应当尽可能小,于是T={a1,a2,...,ak,x},其中1≤k≤n;于是,$avg(T)=\frac{sum(k)+x}{k+1}$。记$\frac{sum(k)+x}{k+1}=f(k)$,则本问题转换为求解min{f(k)|1≤k≤n}。对于离散型函数,计算其差分:$\Delta f(k)=f(k+1)-f(k)=\frac{a_{k+1}-f(k)}{k+2}$。于是,当Δf(k-1)≤0且Δf(k)≥0时,函数取得极小值,为f(k)。
函数极值问题可以通过二分法求解,每一次求解的时间复杂度为O(logn)。
于是,值为x-f(k)。若这个值大于ans,则更新ans。
参考程序如下:
#include <stdio.h> #include <stdint.h> #define MAX_N 500005 int64_t sum[MAX_N]; int main(void) { int q; scanf("%d", &q); int cnt = 0; double ans = 0; while (q--) { int op; scanf("%d", &op); if (op == 1) { int x; scanf("%d", &x); int low = 0, high = cnt; while (low < high) { int mid = (low + high) / 2; int a = sum[mid + 1] - sum[mid]; double df = a - 1. * (sum[mid] + x) / (mid + 1); if (df > 0) high = mid; else low = mid + 1; } double avg = 1. * (sum[high] + x) / (high + 1); double tmp = 1. * x - avg; if (tmp > ans) ans = tmp; cnt++; sum[cnt] = sum[cnt - 1] + x; } else printf("%.10f\n", ans); } return 0; }