联赛模拟测试5 平均数 二分答案+逆序对
题目描述
分析
之前做过类似的两道题,一道是区间和的\(k\)小值,一道是众数的\(k\)小值
那两道统计的东西都有单调性,可以用两个指针维护,\(O(n)\)计算
但是平均数没有单调性,不能用两个指针去扫
但是这道题的数据范围是 \(10^5\),时间限制是\(2.5s\)
统计答案时还可以再套一个\(log\)
如果当前枚举的平均值是\(mid\)的话
我们只需要找出所有\(\frac{sum[j]-sum[i]}{j-i} \leq mid\)的数对即可
我们把分母乘过去,就变成了\(sum[i]-mid \times i \geq sum[j]-mid \times j\)
可以看成区间求逆序对
用树状数组做需要离散化,用归并排序比较方便
代码
#include<cstdio>
#include<queue>
#include<algorithm>
typedef double db;
const int maxn=1e6+5;
const db eps=1e-6;
int n,a[maxn];
long long sum[maxn],m,js;
db b[maxn],c[maxn];
void msort(int l,int r){
if(l==r) return;
int mids=(l+r)>>1;
msort(l,mids);
msort(mids+1,r);
int now=l,t=l-1;
for(int i=mids+1;i<=r;i++){
while(b[now]<b[i] && now<=mids){
c[++t]=b[now++];
}
js+=mids-now+1;
c[++t]=b[i];
}
while(now<=mids) c[++t]=b[now++];
for(int i=l;i<=r;i++) b[i]=c[i];
}
bool jud(db mids){
for(int i=0;i<=n;i++){
b[i]=sum[i]-i*mids;
}
js=0;
msort(0,n);
if(js>=m) return 1;
else return 0;
}
int main(){
freopen("ave.in","r",stdin);
freopen("ave.out","w",stdout);
scanf("%d%lld",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
for(int i=1;i<=n;i++){
sum[i]=sum[i-1]+a[i];
}
double l=-1e9,r=1e9,mids;
while(r-l>eps){
mids=(l+r)/2.0;
if(jud(mids)) r=mids;
else l=mids;
}
printf("%.4f\n",mids);
return 0;
}