其他分享
首页 > 其他分享> > Codeforces Round #464 (Div. 2) E. Maximize!

Codeforces Round #464 (Div. 2) E. Maximize!

作者:互联网

You are given a multiset S consisting of positive integers (initially empty). There are two kind of queries:

1.Add a positive integer to S, the newly added integer is not less than any number in it.
2.Find a subset s of the set S such that the value max(s) - mean(s) is maximum possible. Here max(s) means maximum value of elements in s, mean(s) — the average value of numbers in s. Output this maximum possible value of max(s) - mean(S).

Input:

The first line contains a single integer Q (1 ≤ Q ≤ 5·105) — the number of queries.

Each of the next Q lines contains a description of query. For queries of type 1 two integers 1 and x are given, where x (1 ≤ x ≤ 109) is a number that you should add to S. It’s guaranteed that x is not less than any number in S. For queries of type 2, a single integer 2 is given.

It’s guaranteed that the first query has type 1, i. e. S is not empty when a query of type 2 comes.
--------------------------------------------------------------------------------------------

Output

Output the answer for each query of the second type in the order these queries are given in input. Each number should be printed in separate line.

Your answer is considered correct, if each of your answers has absolute or relative error not greater than 10 - 6.
--------------------------------------------------------------------------------------------

Examples:

Input

6
1 3
2
1 4
2
1 8
2

Output

0.0000000000
0.5000000000
3.0000000000

Input

4
1 1
1 4
1 5
2

Output

2.0000000000

题意大概就是依次输入一组递增的数,随时进行查询,找出最大值减平均值的差最大的子集,并输出该值。、

先说结果:每次需要进行查询的时候,最优子集一定是当前的最大值和最前面的n个数所组成的子集{a1, a2, … an, amax}。

证明的话,可以用反证法:可以证明,若不是上述子集,那么一定可以找到一个比它更优的子集。

  1. 首先,从集合中任选一个子集,容易证明,把该子集的最大值换成整个集合的最大值的话可以得到更优解。
    证:设集合有n个数,集合的最大值和子集的最大值的差为k,那么替换之后子集平均值增加 k/n ,k - k/n >= 0。故该子集的最大值换成整个集合的最大值的话必定得到更优解。

  2. 此时我们可以得到,最优子集一定由集合最大值和其它数组成。接着再来说明其他数一定是最前面的n个数。
    证:
    (1) 设子集的平均数为a(注意在下列证明过程中,a会不断发生变化),考虑那些在a以上的除了最大值以外的数,这些数统称为up,若把up从子集里去除,易证明新得到的子集是更优解(平均值减少,最大值不变)。把up去除后继续下一步。
    (2) 那么此时子集里除了最大值,剩下的数都小于a。把小于a的数统称为down,若把down添加到子集中,易证明新得到的子集是更优解(同上)。
    (3)注意到当我们进行第(2)步之后,可能有一些本来在down里的数会大于a,这时候就要再次进行步骤(1)。
    (4) 多次进行(1) (2) 步骤,最终的得到的子集就是当前的最大值和最前面的n个数所组成的子集

证明完成,所以我们只要找到要取前几个数就可以了。

最终的代码:

因为题目给的数据是递增的,所以随着数据的输入,需要的数据个数也是递增的,所以这里用了一个min_pos来记录最左的位置。
好像别人用的是三分去求值,但我没有用三分,也没用其他的什么算法。

/**
 * TODO Auther: by SR
 * FilePath: \vscodefile\ACM\2019winter_vacation_training\day1div2\M.cpp
 * Date: 2020-02-05 17:11:37
 * LastEditTime: 2020-02-06 11:42:59
 * ! 今日运势: 吉,无bug
 * 最优子集就是只有最大值大于平均值的最大子集{1,2,...,max} ,//?就是取最大值和最前面的n个数就行了
**/
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstdio>
#include<string>
#include<memory.h>
#include<iomanip>
using namespace std;
typedef long long ll;
const int MAXN = 5e5;
const int INF = 0x3f3f3f3f;

void solve();
double count_average(const int right);

int cnt = 0, min_pos = 1;
double num[MAXN+10] = {0}, aver[MAXN+10] = {0};

int main(){
    int T,q;
    
    cin>>T;
    for(int i = 0; i <= T; i++){
            aver[i] = 0;
    }
    while(T--){
        bool flag = true;
        scanf("%d", &q);
        if(q==2){
            solve();
            continue;
        }
        else{
            scanf("%lf", &num[++cnt]);
            if(flag==true){
                flag = false;
                aver[1] = num[1];
            }
        }
    }
	return 0;
}

void solve(){
    int r = min_pos;
    double average = count_average(r), temp;
    while(r+1 <= cnt && num[r+1] <= (temp = count_average(r+1))){
        r++;
        average = temp;
    }
    min_pos = r;

    //cout<<"r == "<<r<<"cnt == "<<cnt<<"\n";

    printf("%.10lf\n", num[cnt] - average);
    return;
}

double count_average(const int right){
    if(aver[right] != 0){
        if(right == cnt){
            return aver[right];
        }
        else{
            return (aver[right]*double(right) + num[cnt]) / double(right+1);
        }
        
    }
    for(register int i = right; i >= 1; i--){
        if(aver[i] != 0){
            double average = aver[i] * i;
            for(register int j = i+1; j <= right; j++){
                average += num[j];
            }
            aver[right] = average / double(right);

            if(right == cnt){
            return aver[right];
            }
            else{
                return (aver[right]*double(right) + num[cnt]) / double(right+1);
            }
        }
    }
    
}
limited_sky 发布了5 篇原创文章 · 获赞 0 · 访问量 1321 私信 关注

标签:int,464,最大值,Codeforces,number,子集,queries,Div,include
来源: https://blog.csdn.net/limited_sky/article/details/104194108