集合操作(双指针,贪心)
题意
给定一个由正整数(最初为空)组成的多重集\(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;
}