Codeforces 626 E Simple Skewness —— 二分

91 篇文章 0 订阅
27 篇文章 1 订阅

This way

题意:

给你一些数,让你找出几个数,使得这些数的平均数-中位数最大,问你这些数是哪些。

题解:

奇数:

奇数的话,中位数便非常好确定了,那么我们肯定是让排序后中位数右边的数尽可能大,并且左边的数也尽可能大,这样的话平均数也会变大。如果我们要枚举每个数是中位数的话,再枚举长度就是n^2的时间复杂度了,所以要优化。但是我们发现,随着长度变长,新加进来的两个数的和一定是越来越小的(黑箭头代表中位数的位置,黑色区间代表原来取的中位数左边和右边的值区间,因为我们要使小于等于中位数的值尽可能地大,又要使大于等于中位数的值尽可能的大。红色代表左边的数数量+1且右边的数数量+1):
在这里插入图片描述
所以这个函数的曲线是这样的:
在这里插入图片描述
三分的图,但是对于诶个点是整数的时候,可以用二分代替三分:比较mid和mid-1的大小即可。
但是不能直接求平均数,会有精度的问题,所以用交叉相乘即可。

偶数

对于偶数的情况,我们考虑这是否可能,因为如果不可能的话就不用做了哈哈
首先我们假设左边的中位数是al,右边的中位数是ar,数的数量是s,那么现在的答案是 1 + . . + a l + a r + . . . + a s s − a l + a r 2 \frac{1+..+al+ar+...+as}{s}-\frac{al+ar}{2} s1+..+al+ar+...+as2al+ar
如果将ar去掉,答案变成 1 + . . . + a l + . . + a s s − 1 − a l \frac{1+...+al+..+as}{s-1}-al s11+...+al+..+asal用上面的-下面的,就变成了
( s − 1 ) ∗ a r − ( a 1 + . . . + a l + . . . + a s ) s ∗ ( s − 1 ) − a r − a l 2 \frac{(s-1)*ar-(a1+...+al+...+as)}{s*(s-1)}-\frac{ar-al}{2} s(s1)(s1)ar(a1+...+al+...+as)2aral
用高数的那种什么方法,就是说一个数变小之后还是大于等于另一个数,那么这个数就大雨另一个数。
所以因为2是一定小于等于s*(s-1)的,所以我们假设右边变成 a r − a l s ∗ ( s − 1 ) \frac{ar-al}{s*(s-1)} s(s1)aral,那么就变成了
( s − 1 ) ∗ a r + a l − ( a 1 + . . . + a l + a r + . . . + a s ) s ∗ ( s − 1 ) \frac{(s-1)*ar+al-(a1+...+al+ar+...+as)}{s*(s-1)} s(s1)(s1)ar+al(a1+...+al+ar+...+as),那么可以看成 ( s − 1 ) ∗ a r + a l (s-1)*ar+al (s1)ar+al ( a 1 + . . . + a l + a r + . . . + a s ) 这两个数的比较 (a1+...+al+ar+...+as)这两个数的比较 (a1+...+al+ar+...+as)这两个数的比较,那么变成 ( s − 1 ) ∗ a r (s-1)*ar (s1)ar a 1 + . . . + a r + . . . + a s a1+...+ar+...+as a1+...+ar+...+as这两个数的比较,于是又变成
a r ar ar a 1 + . . . + a r + a s s − 1 \frac{a1+...+ar+as}{s-1} s1a1+...+ar+as这两个数的比较,于是我们可以看出,ar是 a 1 + . . . + a r + . . . + a s a1+...+ar+...+as a1+...+ar+...+as的中位数(因为al已经被消掉了),所以这两个数就是平均数与中位数的大小比较,同时我们知道,平均数应该一定大于等于中位数,否则你所求的东西将毫无意义。于是我们可以得出,奇数一定比偶数更优。
于是不用考虑偶数的情况。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=2e5+5;
ll a[N],pre[N];
int n;
ll check(int p,int len)
{
    return pre[p]-pre[p-len-1]+pre[n]-pre[n-len];
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
    sort(a+1,a+1+n);
    for(int i=1;i<=n;i++)
        pre[i]=pre[i-1]+a[i];
    int p=1,len=0;
    ll ans=0;
    for(int i=1;i<=n;i++)
    {
        int l=1,r=min(i-1,n-i),mid;
        while(r>=l)
        {
            mid=l+r>>1;
            if((ll)check(i,mid)*(mid*2-1)>=(ll)check(i,mid-1)*(mid*2+1))
            {
                l=mid+1;
                if((ll)check(i,mid)*(len*2+1)-a[i]*(len*2+1)*(mid*2+1)>(ll)ans*(mid*2+1)-a[p]*(len*2+1)*(mid*2+1))
                    ans=check(i,mid),p=i,len=mid;
            }
            else
                r=mid-1;
        }
    }
    printf("%d\n",len*2+1);
    for(int i=p-len;i<=p;i++)
        printf("%d ",pre[i]-pre[i-1]);
    for(int i=n-len+1;i<=n;i++)
        printf("%d ",pre[i]-pre[i-1]);
    printf("\n");
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值