51NOD 1711平均数

题目大意

有一个长度为n的序列a,求所有区间的平均数中第k大。

看题第一眼

什么鬼啊?!!

二分

可以想一下二分答案ans,就是要求平均数大于ans的区间个数有多少

求平均数大于ans的区间个数

首先转化成数学式子: sumisumjijans
那么式子可以再转化为:
sumisumjans×(ij)
sumisumjans×ians×j
sumians×isumjans×j
那么两边都分别只与i和j有关,于是我们可以将所有的 sumians×i 离散化,然后用个树状数组统计一下就好了

我打题时一开始没有把0也插入进去就错了啊,然后二分范围也要适当大一点,还有eps。

题目链接:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1711

贴代码:

#include
#include
#include
#include
#include

#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)

using namespace std;

typedef long long LL;
typedef double db;

const int N = 100010;
const db eps = 1e-5;

LL a[N],k;
int num[N],level[N];
db v[N];
int n;
db l,r,mid,ans;
int tree[N];

bool cmp(int x,int y){
    return v[x]int get_t(int x){
    int tot=0;
    while(x){
        tot+=tree[x];
        x-=x&-x;
    }
    return tot;
}

void add(int x){
    while(x<=n){
        tree[x]++;
        x+=x&-x;
    }
}

LL gettot(db x){
    fo(i,1,n)v[i]=db(a[i])-x*i;
    sort(num,num+1+n,cmp);
    int u=0;
    fo(i,0,n){
        if (i==0||v[num[i]]-v[num[i-1]]>eps)u++;
        level[num[i]]=u;
    }
    fo(i,1,n)tree[i]=0;
    LL tot=0;
    add(level[0]);
    fo(i,1,n){
        tot+=get_t(level[i]);
        add(level[i]);
    }
    return tot;
}

int main(){
    freopen("average.in","r",stdin);
    freopen("average.out","w",stdout);
    scanf("%d%lld",&n,&k);
    fo(i,1,n)scanf("%lld",&a[i]);
    fo(i,1,n){
        a[i]+=a[i-1];
        num[i]=i;
    }
    l=0,r=100001,ans=0;
    while(l+eps<=r){
        mid=(l+r)/2;
        if (gettot(mid)>=k)l=mid+eps;
        else{
            r=mid-eps;
            ans=mid;
        }
    }
    printf("%.4lf\n",ans);
    fclose(stdin);
    fclose(stdout);
    return 0;
}

你可能感兴趣的:(解题报告,数据结构)