[BZOJbegin][NOIP十连测第三场]平均数(二分+归并排序求逆序对)

45 篇文章 0 订阅
3 篇文章 0 订阅

题目描述

这里写图片描述

题解

首先二分一个答案k,将序列中的数都减去k,然后求前缀和。
可以发现平均数小于k的子序列只可能是 Si>Sji<j 的。也就是序列中的逆序对数。
因为是实数二分+判定,用归并排序求逆序对即可。
注意判断精度。

代码

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define N 100005
#define LL long long

const double eps=1e-6;
int n;
LL k,rev;
double Max,ans;
double a[N],b[N],c[N],s[N];

int dcmp(double x)
{
    if (x<=eps&&x>=-eps) return 0;
    if (x>0) return 1;
    else return -1;
}
void mergesort(int l,int r)
{
    if (l==r) return;
    int mid=(l+r)>>1;
    mergesort(l,mid);
    mergesort(mid+1,r);

    int i=l,j=mid+1,now=l-1;
    while (i<=mid&&j<=r)
    {
        if (dcmp(s[j]-s[i])>=0) c[++now]=s[i],i++;
        else c[++now]=s[j],j++,rev+=mid-i+1;
    }
    while (i<=mid) c[++now]=s[i],i++;
    while (j<=r) c[++now]=s[j],j++;
    for (int i=l;i<=r;++i) s[i]=c[i];
}
bool check(double x)
{
    for (int i=1;i<=n;++i) b[i]=a[i]-x;
    memset(s,0,sizeof(s));
    for (int i=1;i<=n;++i) s[i]=s[i-1]+b[i];

    rev=0;
    for (int i=1;i<=n;++i) if (dcmp(s[i])<=0) rev++;
    mergesort(1,n);
    if (rev<k) return false;
    else return true;
}
double find()
{
    double l=0,r=Max,mid,ans;
    while (dcmp(r-l)!=0)
    {
        mid=(l+r)/(2.0);
        if (check(mid)) ans=mid,r=mid;
        else l=mid;
    }
    return ans;
}
int main()
{
    scanf("%d%lld",&n,&k);
    for (int i=1;i<=n;++i) scanf("%lf",&a[i]),Max=max(Max,a[i]);

    ans=find();
    printf("%0.4lf\n",ans);
}

总结

①减去一个数求前缀和——平均数常用套路。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值