原题链接

集合操作

题目性质

假设从SS中的一个答案子集s={x1,x3,,xn1,xm}s=\{ x_1,x_3,\cdots,x_{n-1},x_m\},其中xmx_m为最大值,长度为nn

注意这个n,mn,m和题目中的并不相同,这里但指集合ss的属性

  1. 一个集合ss的答案值max(s)mean(s)max(s)-mean(s)

res=xmx1+x2++xn1+xmn=xmx1n+xmx2n++xmxn1nres=x_m-\frac{x_1+x_2+\cdots+x_{n-1}+x_m}{n}=\frac{x_m-x_1}{n}+\frac{x_m-x_2}{n}+\cdots+\frac{x_m-x_{n-1}}{n}

  1. 每次选出的集合ss要包含新加入的元素xMx_M

    xmx_m是当前集合上一个的最大值

    答案集合s={x1,x3,,xn1,xm}s=\{ x_1,x_3,\cdots,x_{n-1},x_m\},则将xMx_M去替换xmx_m我们可以知道

    新的集合s1={x1,x3,,xn1,xM}s_1=\{ x_1,x_3,\cdots,x_{n-1},x_M\}resres一定不会比ssresres要小

    可以带入上面的公式试一试哦

  2. 我们选择的集合ss一定是从从SS中选择最小的n1n-1个元素加上最大的一个元素xmx_m构成

    通过resres表达式可知当我们确定选nn个元素时候选最小的n1n-1个元素且xmx_m选最大的那个元素一定比选择其他元素得到的大

    或者 我们需要平均值最小 那么选的数肯定也是最小的

  3. 添加元素

我们发现我们需要的子集一定是从第一个元素开始的有序子集,需要从小到大依次的添加一个数,但是加到哪一个位置就不能加了呢?

假设一个集合s1={x1,x3,,xn1,xm}s_1=\{ x_1,x_3,\cdots,x_{n-1},x_m\}我们尝试加入xnx_n使s2={x1,x3,,xn1,xn,xm}s_2=\{ x_1,x_3,\cdots,x_{n-1},x_n,x_m\}

ress1=xmx1n+xmx2n++xmxn1nres_{s_1}=\frac{x_m-x_1}{n}+\frac{x_m-x_2}{n}+\cdots+\frac{x_m-x_{n-1}}{n}

ress2=xmx1n+1+xmx2n+1++xmxn1n+1+xmxnn+1res_{s_2}=\frac{x_m-x_1}{n+1}+\frac{x_m-x_2}{n+1}+\cdots+\frac{x_m-x_{n-1}}{n+1}+\frac{x_m-x_n}{n+1}

当我们发现ress1ress2<0res_{s_1}-res_{s_2}<0的时候我们就可以加入xnx_n,即:

(xmx1nxmx1n+1)+(xmx2nxmx2n+1)++(xmxn1nxmxn1n+1)<xmxnn+1(\frac{x_m-x_1}{n}-\frac{x_m-x_1}{n+1})+(\frac{x_m-x_2}{n}-\frac{x_m-x_2}{n+1})+\cdots +(\frac{x_m-x_{n-1}}{n}-\frac{x_m-x_{n-1}}{n+1})<\frac{x_m-x_n}{n+1}

进一步化简话

xmx1n(n+1)+xmx2n(n+1)++xmxn1n(n+1)<xmxnn\frac{x_m-x_1}{n(n+1)}+\frac{x_m-x_2}{n(n+1)}+\cdots+\frac{x_m-x_{n-1}}{n(n+1)}<\frac{x_m-x_n}{n}

xmx1n+1+xmx2n+1++xmxn1n+1<xmxn\frac{x_m-x_1}{n+1}+\frac{x_m-x_2}{n+1}+\cdots+\frac{x_m-x_{n-1}}{n+1}<x_m-x_n

nxmsum(x1,x2,,xn)<(n+1)(xmxn)nx_m-sum(x_1,x_2,\cdots,x_n)<(n+1)(x_m-x_n)

xm+sum(x1,x2,,xn1)>(n+1)xnx_m+sum(x_1,x_2,\cdots,x_{n-1})>(n+1)x_n

所以说当我们需要添加的元素满足xm+sum(x1,x2,,xn1)>(n+1)xnx_m+sum(x_1,x_2,\cdots,x_{n-1})>(n+1)x_n则该元素就可以将xnx_n添加进来

  1. 删除元素

    根据上面四条我们可以确定我们需要的元素是从小到大列举的 如果想删除某一个元素 肯定是删除最后添加的元素(不是最大值),但是我们在进行第四步的时候可以不把这个元素添加进来呀,添加进来的就是最优的情况,所以我们不需要删除元素,只判断哪一个元素是否可以添加就行了

实际代码

a[N]记录读入的数字 ,s记录前nn个元素和,k当前集合中已经加入到哪个位置上了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include<iostream>
using namespace std;
const int N=5e5+5;
int q,x,a[N],k,n;
double s;
int main(){
scanf("%d",&q);
while(q--){
scanf("%d",&x);
if(x==1)scanf("%d",&a[++n]);
else{
while(k+1<=n&&a[k+1]<(s+a[n])/(k+1))s+=a[++k];
printf("%0.6f\n",a[n]-(a[n]+s)/(k+1));
}
}
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include<iostream>
using namespace std;
const int N=5e5+5;
int q,x,a[N],k,n;
double s;
int main(){
scanf("%d",&q);
while(q--){
scanf("%d",&x);
if(x==1){
scanf("%d",&a[++n]);
while(k+1<=n&&a[k+1]<(s+a[n])/(k+1))s+=a[++k];
}else printf("%0.6f\n",a[n]-(a[n]+s)/(k+1));
}
return 0;
}

发个牢骚

第一次打周赛,迟到了五分种,以至于这道题就差最后三分钟可以提交,不过幸好的是一遍AC没有改错哈哈哈

第 62 场周赛 - AcWing