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

博客围绕LYK的长度为n的序列a展开,其想知道所有区间的平均数,因区间数目多,只需告知所有区间(共n*(n+1)/2个)中第k大的平均数,还给出了输入、输出及样例。

LYK有一个长度为n的序列a。

他最近在研究平均数。

他甚至想知道所有区间的平均数,但是区间数目实在太多了。

为了方便起见,你只要告诉他所有区间(n*(n+1)/2个区间)中第k大的平均数就行了。

 收起

输入

第一行两个数n,k(1<=n<=100000,1<=k<=n*(n+1)/2)。
接下来一行n个数表示LYK的区间(1<=ai<=100000)。

输出

一行表示第k大的平均数,误差不超过1e-4就算正确。

输入样例

5 3
1 2 3 4 5

输出样例

4.000
/*
@Author: Top_Spirit
@Language: C++
*/
#include <bits/stdc++.h>
using namespace std ;
typedef long long ll ;
const int Maxn = 1e5 + 100 ;
const double eps = 1e-6 ;

int low[Maxn], a[Maxn] ;
int n ;
ll k ;
double sum[Maxn], tmp[Maxn] ;
int pos[Maxn] ;

inline int lowbit (int i) { return i & (-i); }

inline void add(int k){
    while (k <= n){
        low[k]++ ;
        k += lowbit(k) ;
    }
}

inline ll getNum(int i){
    ll ans = 0 ;
    while (i > 0){
        ans += low[i] ;
        i -= lowbit(i) ;
    }
    return ans ;
}

vector < double  > ve ;

int getid(double x){
    return lower_bound(ve.begin(), ve.end(), x) - ve.begin() + 1 ;
}

bool check (double mid){
    ve.clear() ;
    for (int i = 1; i <= n; i++){
        tmp[i] = 1.0 * sum[i] - mid * i ;
        ve.push_back(tmp[i]) ;
        low[i] = 0 ;
    }
    ve.push_back(0.0) ;
    sort(ve.begin(), ve.end()) ;
    ve.erase(unique(ve.begin(), ve.end()), ve.end()) ;
    for (int i = 1; i <= n; i++){
        pos[i] = getid(tmp[i]) ;
    }
    add(getid(0)) ;
    ll cnt = 0 ;
    for (int i = 1; i <= n; i++){
        cnt += getNum(pos[i]) ;
        add(pos[i]) ;
    }
    if (cnt >= k) return true ;
    else return false ;
}

int main (){
    cin >> n >> k ;
    sum[0] = 0 ;
    double l = 1e9, r = 0 ;
    for (int i = 1; i <= n; i++){
        cin >> a[i] ;
        sum[i] = sum[i - 1] + a[i] ;
        l = min(a[i] * 1.0, l) ;
        r = max(a[i] * 1.0, r) ;
    }
    double ans = l ;
    for (int i = 1; i <= 40; i++) {
        double mid = (l + r) / 2.0 ;
        if (check(mid)) {
            l = mid ;
            ans = l ;
        }
        else r = mid ;
    }
    cout << fixed << setprecision(10) << ans << endl ;
    return 0 ;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值