集合操作(双指针,贪心)

题意

给定一个由正整数(最初为空)组成的多重集\(S\)。多重集表示可能存在重复元素的集合。

请你对该集合执行\(Q\)次操作,操作分为两种:

  • 增加操作,格式为1 x,将一个正整数\(x\)加入到集合\(S\)中。数据保证,\(x\)不小于当前\(S\)中的任何元素。
  • 询问操作,格式为2,找到一个当前\(S\)的子集\(s\),要求\(max(s)−mean(s)\)的值应尽可能大,输出\(max(s)−mean(s)\)的最大可能值。\(max(s)\)表示\(s\)中最大元素的值,\(mean(s)\)表示\(s\)中所有元素的平均值。

题目链接:https://www.acwing.com/problem/content/4505/

数据范围

\(1 \leq Q \leq 5 \times 10^5\)
\(1 \leq x \leq 10^9\)

思路

这里给出三条结论,不进行详细证明了(好像也不难证):

  • 集合中的元素一定包含新加入的元素,以及最小的\(k\)个元素

  • 最终答案关于\(k\)的函数一定是先增后减的

  • 新加入元素之后,最优的\(k\)是不减的。

因此,用一个指针指向\(k\),维护使得均值最小的\(k\)

详细证明见:https://www.acwing.com/solution/content/128919/

代码

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>

using namespace std;

typedef long long ll;

const int N = 500010;

int n;
ll a[N];
int cnt;

int main()
{
    scanf("%d", &n);
    double s = 0;
    int k = 0, op;
    while(n --) {
        scanf("%d", &op);
        if(op == 1) {
            int x;
            scanf("%d", &x);
            cnt ++, a[cnt] = x;
        }
        else {
            while(k + 1 <= cnt && (s + a[cnt]) / (k + 1) > a[k + 1]) {
                k ++;
                s += a[k];
            }
            printf("%.6f\n", a[cnt] - (s + a[cnt]) / (k + 1));
        }
    }
    return 0;
}
posted @ 2022-08-16 11:50  pbc的成长之路  阅读(19)  评论(0编辑  收藏  举报