51nod 平均数(二分+离散+树状数组)

alpq654321  (命题人)
基准时间限制:4 秒 空间限制:131072 KB 分值: 80
LYK有一个长度为n的序列a。
他最近在研究平均数。
他甚至想知道所有区间的平均数,但是区间数目实在太多了。
为了方便起见,你只要告诉他所有区间(n*(n+1)/2个区间)中第k大的平均数就行了。
Input
第一行两个数n,k(1<=n<=100000,1<=k<=n*(n+1)/2)。
接下来一行n个数表示LYK的区间(1<=ai<=100000)。
Output
一行表示第k大的平均数,误差不超过1e-4就算正确。
Input示例
5 3
1 2 3 4 5
Output示例

4.000

先二分答案,将n个数中的最大值作为二分的上边界

设二分的答案为t,
我们可以新建一个数组f表示所有前i个数(ai-t)的和,则f[i]<0表示1~i这个区间的平均数小于t。
则f[i]==0表示1~i这个区间的中位数等于t。
则f[i]>0表示1~i这个区间的中位数等于t。
再确立这样一个事实,假设 一个区间1~x有x个元素,其中和是sum1。
另一个区间1~y(假设x<y),有y个元素,其中和为sum2.若sum2<sum1,则区间x~y的sum<0,既区间x~y的平均数小于t。
接下来下面这个f(i)
f(i)123456
sum-12051-3
自己根据上面结论推一下,就会发现,这6个元素区间大于t的区间个数,tm不就是sum这个数组前后反一下的的逆序数?
逆序数嘛,树状数组随便搞下就好,不过这题的f(i)可能会超LL,,要离散化一下
下面上代码“
#include <stdio.h>
#include <iostream>
#include<algorithm>
#include <string.h>
#include<math.h>
using namespace std;
typedef long long LL;
#define N 100000
#define exp 1e-6
LL n,k;
int tree[N+10];
double sz[N+10];
struct Node {
    double val;
    int pos;
} f[N+10];
bool cmp(Node a,Node b) {
    return a.val<b.val;
}
int lowbit(int x) {
    return x&-x;
}
void add(int x,int v) {
    while(x<=n) {
        tree[x]+=v;
        x+=lowbit(x);
    }
}
int Sum(int x) {
    int tot=0;
    while(x) {
        tot+=tree[x];
        x-=lowbit(x);
    }
    return tot;
}
bool solve(double t) {
    memset(tree,0,sizeof(tree));
    f[1].val=sz[1]-t;
    f[1].pos=1;
    for(int i=2; i<=n; i++) {
        f[i].val=f[i-1].val+sz[i]-t;
        f[i].pos=i;//离散化
    }
    sort(f+1,f+n+1,cmp);
    LL sum=0;
    for(int i=1; i<=n; i++) {
        if(f[i].val>=0)sum++;//这里要注意,,wa了好久。。
        sum+=Sum(f[i].pos);
        add(f[i].pos,1);
    }
    if(sum>=k) return 1;
    else return 0;

}
int main() {
    //freopen("input.txt","r",stdin);
    scanf("%I64d%I64d",&n,&k);
    double r;
    for(int i=1; i<=n; i++) {
        scanf("%lf",&sz[i]) ;
        r=max(r,sz[i]);
    }
    double mid,l=0;
    while(r-l>exp) {
        mid=((r+l)/2);
        if(solve(mid))l=mid;
        else r=mid;
    }//二分
    printf("%.3f\n",r);
    return 0;
}




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值