zoukankan      html  css  js  c++  java
  • Codeforces 939E Maximize ( 三分 || 二分 )

    题意 : 给出两个操作,① 往一个序列集合(初始为空)里面不降序地添加数字、② 找出当前序列集合的一个子集使得 (子集的最大元素) - (子集的平均数) 最大并且输出这个最大差值

    分析 : 

    首先关注到 ① 操作是有序地添加数

    然后为了回答 ② 的问询,来分析一波

    直觉告诉我们,要最大化差值,选取的子集最大元素应当越大越好

    这一点是对的,具体的证明可以看 CF 的官方题解

    那么也就是说选出的子集里面必定有当前序列集合的最大值元素

    然后为了使(子集的平均数)越小,直觉又告诉我们

    需要贪心地选择小的元素加入子集,这一点是显然的

    仔细一想就能发现,如果我们从小到大地将元素加入子集

    子集的平均数肯定是先减后增,是个凹函数

    又因为是序列有序,所以我们可以去三分序列的前缀和数组寻找凹点

    最后的答案就是 (集合序列最大值) - (凹点的平均值)

    至于二分解法,也同样是和上面一个道理

    我们可以去二分前缀和数组的一个位置,假设为 POS

    如果算出来的平均值比 POS+1 这个位置的元素更大

    说明加入 POS+1 这个元素肯定更优,最后一直二分到合理位置就是答案了

    具体看代码

    二分 1231ms

    #include<bits/stdc++.h>
    #define LL long long
    using namespace std;
    const int maxn = 5e5 + 10;
    vector<LL> arr;
    LL Presum[maxn];
    
    inline void GetAns()
    {
        int L = 0, R = arr.size()-2, idx = 0;
        double avg;
        while(L <= R){
            int mid = (R + L) / 2;
            avg = (double)(arr[arr.size()-1] + Presum[mid]) / (double)(mid + 2);
            if(avg > arr[mid+1]) L = mid + 1;///平均值比后面的元素更大,说明添加进 mid+1 这个元素肯定更优
            else R = mid - 1, idx = mid;
        }
    
        avg = (double)(arr[arr.size()-1] + Presum[idx]) / (double)(idx + 2);
        double ans = (double)arr[arr.size()-1] - avg;
        printf("%.9f
    ", ans);
    }
    
    int main(void)
    {
        int Q;
        scanf("%d", &Q);
        while(Q--){
            int command;
            scanf("%d", &command);
            if(command == 1){
                LL tmp;
                cin>>tmp;
                arr.push_back(tmp);
                Presum[arr.size()-1] = ((arr.size()-2 < 0) ? 0 : Presum[arr.size()-2]) + tmp;
            }else GetAns();
        }
        return 0;
    }
    二分

     三分 514ms

    #include<bits/stdc++.h>
    #define LL long long
    using namespace std;
    const int maxn = 5e5 + 10;
    vector<LL> arr;
    LL Presum[maxn];
    
    double Fun(int pos)
    { return (double)(arr[arr.size()-1] + Presum[pos]) / (double)(pos + 2); }
    
    double GetAns()
    {
        int L = 0, R = arr.size() - 2;
        while(L < R-1){
            int mid = (L + R) / 2;
            int mmid = (mid + R) / 2;
            if( Fun(mid) > Fun(mmid) ) L = mid;
            else R = mmid;
        }
        return (double)arr[arr.size()-1] - Fun(Fun(L) > Fun(R) ? R : L);
    }
    
    int main(void)
    {
        int Q;
        scanf("%d", &Q);
        while(Q--){
            int command;
            scanf("%d", &command);
            if(command == 1){
                LL tmp;
                scanf("%I64d", &tmp);
                arr.push_back(tmp);
                Presum[arr.size()-1] = (arr.size()-2 < 0 ? 0 : Presum[arr.size()-2]) + tmp;
            }else{
                printf("%.9f
    ", GetAns());
            }
        }
        return 0;
    }
    三分
  • 相关阅读:
    eclipse自动切换到debug视图
    Android Studio 1.1.0 “关联源码” 或者“导入源码” ,又或者插件包
    Eclipse中如何安装和使用GrepCode插件 (转)
    转【Python】同时向控制台和文件输出日志logging
    AngularJs学习
    MongoDB聚合运算之mapReduce函数的使用(11)
    MongoDB聚合运算之group和aggregate聚集框架简单聚合(10)
    MongoDB的分片(9)
    MongoDB replication set副本集(主从复制)(8)(转)
    MongoDB的导入导出(7)
  • 原文地址:https://www.cnblogs.com/qwertiLH/p/8560102.html
Copyright © 2011-2022 走看看