JZOJ 4869 平均数

来源:互联网 发布:mac mini散热器 编辑:程序博客网 时间:2024/05/05 11:38

题目大意

给出长度为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