2nd SCPC - L 破坏守序序列(数学证明+三分)

在这里插入图片描述
首先如果序列为奇数,那么可以考虑枚举中位数,但是如果序列为偶数呢,这个如果枚举的话一定超时,但是又没什么好的办法,那么就证明一下答案序列一定为奇数

图片来自Visors(懒得手敲了好麻烦):
在这里插入图片描述
有了上述证明,那么我们枚举每个数作为中位数,在能取到的情况下,每次同时取 k k k个比它大的和 k k k比它小的组成序列

但是如果这里仍然枚举的话,时间复杂度仍是 O ( n 2 ) O(n^2) O(n2),过不了

首先假设中位数为 a m a_m am,已经取了比它小的最小的 a 1 a_1 a1和最大的 a n a_n an,那么平均数为:

a 1 + a m + a n 3 \frac{a_1 + a_m + a_n }{3} 3a1+am+an

加下来我们尝试再加入一组数 a 2 , a n − 1 a_2,a_{n-1} a2,an1,平均数为:

a 1 + a 2 + a m + a n − 1 + a n 5 = a 1 + a m + a n 3 ∗ 3 5 + a 2 + a n − 1 5 \frac{a_1 + a_2+ a_m + a_{n-1}+a_n }{5}=\frac{a_1+ a_m +a_n }{3}*\frac{3}{5}+\frac{a_2+ a_{n-1} }{5} 5a1+a2+am+an1+an=3a1+am+an53+5a2+an1

因此不难观察到平均数每次的增量应该是越来越小且可能会变为负增量,而中位数是不变的,那么答案应该是先递增后递减的

或者这样,由于每加入一组数的和是递减的,而除数是递增的,那么如果列出函数图像的话,应该是一个凸函数图像,这种函数关系显然需要三分查找解决

PS:在判断 a b > c d \frac{a}{b} > \frac{c}{d} ba>dc时,没必要转化为浮点数,直接判断 a ∗ d > b ∗ c a*d>b*c ad>bc即可

#include <set>
#include <map>
#include <stack>
#include <queue>
#include <math.h>
#include <cstdio>
#include <string>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <unordered_map>
using namespace std;
#define lowbit(x) (x&(-x))
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> P;
const double eps=1e-8;
const double pi=acos(-1.0);
const int inf=0x3f3f3f3f;
const ll INF=1e18;
const int Mod=1e9+7;
const int maxn=1e6+10;

int n;
int a[maxn];
ll sum[maxn];

ll f(int i){
    return sum[i]+sum[n]-sum[n-i];
}

double solve(int x,int pos){
    int l=1,r=min(pos-1,n-pos);
    while(l+10<r){
        int midl=l+(r-l)/3;
        int midr=r-(r-l)/3;
        if((f(midl)+x)*(2*midr+1)>(f(midr)+x)*(2*midl+1)) r=midr;
        else l=midl;
    }
    double ans=0.0;
    for(int i=l;i<=r;i++)
        ans=max(ans,(double)(f(i)+x)/(2*i+1)-x);
    return ans;
}

int main(){
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    //ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    sort(a+1,a+n+1);
    for(int i=1;i<=n;i++) sum[i]=sum[i-1]+a[i];
    double ans=0.0;
    for(int i=1;i<=n;i++){
        ans=max(ans,solve(a[i],i));
    }
    printf("%.2f\n",ans);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值