Codeforces 939E - Maximize!

传送门:http://codeforces.com/contest/939/problem/E

本题是一个序列上的问题。

一个序列S,初始时为空。依次给出Q个指令:

①将元素x加入S,保证x≥max(S);

②查询:max{max(T)-avg(T)|TS}。

设在某一次查询时,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

xT中的元素,则:max(T)=xT中的其余元素应当尽可能小,于是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;
}

 

posted on 2018-02-19 20:32  SiuGinHung  阅读(417)  评论(0编辑  收藏  举报

导航