JZOJ 4869 平均数

题目大意

给出长度为n的序列,求所有子区间的和中,第k小的值是多少。

n<=100000
时间限制 1s
空间限制 256M

解题思路

二分答案,每次把序列全部减去mid,问题就变成了求有多少个子区间的和是负数,即sum[i-1]>sum[j],i<=j,求逆序对就可以了。
用线段树会超时,只能用归并排序。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 100006
#define fr(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const double eps=0.00001;

int i,n;
ll k,tot;
double l,r,mid,ans,a[maxn],b[maxn],sum[maxn];
void merge(int l,int r)
{
    int i,mid=(l+r) >> 1,x=l,y=mid,w=mid+1,v=r;
    fr(i,l,r) b[i]=sum[i];
    i=x-1;
    while (x<=y && w<=v)
        if (b[x]<=b[w])
            sum[++i]=b[x++];
        else 
        {
            tot+=mid-x+1;
            sum[++i]=b[w++];
        }
    while (x<=y && w>v) sum[++i]=b[x++];
    while (w<=v && x>y) sum[++i]=b[w++];
    return;
}
void sort(int l,int r)
{
    if (l==r) return;
    int mid=(l+r) >> 1;
    sort(l,mid);
    sort(mid+1,r);
    merge(l,r);
    return;
}
ll check(double mid)
{
    int i;
    tot=sum[0]=0;
    fr(i,1,n) a[i]-=mid;
    fr(i,1,n) sum[i]=sum[i-1]+a[i];
    sort(0,n);
    fr(i,1,n) a[i]+=mid;
    return tot;
}
int main()
{
    freopen("ave.in","r",stdin);
    freopen("ave.out","w",stdout);
    scanf("%d%lld",&n,&k);
    l=1 << 30,r=0;
    fr(i,1,n) 
    {
        scanf("%lf",&a[i]);
        l=min(l,a[i]),r=max(r,a[i]);
    }
    while (r-l>=eps)
    {
        mid=(l+r)/2;
        ll t=check(mid);
        if (t<k) l=mid+eps;
        else r=mid;
    }
    printf("%.4f\n",l);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值