题目大意
有一个长度为n的序列a,求所有区间的平均数中第k大。
看题第一眼
什么鬼啊?!!
二分
可以想一下二分答案ans,就是要求平均数大于ans的区间个数有多少
求平均数大于ans的区间个数
首先转化成数学式子:
sumi−sumji−j≥ans
那么式子可以再转化为:
sumi−sumj≥ans×(i−j)
∴sumi−sumj≥ans×i−ans×j
∴sumi−ans×i≥sumj−ans×j
那么两边都分别只与i和j有关,于是我们可以将所有的
sumi−ans×i
离散化,然后用个树状数组统计一下就好了
我打题时一开始没有把0也插入进去就错了啊,然后二分范围也要适当大一点,还有eps。
题目链接:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1711
贴代码:
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<iostream>
#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]<v[y];
}
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;
}