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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章