1. 程式人生 > 實用技巧 >聯賽模擬測試5 平均數 二分答案+逆序對

聯賽模擬測試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;
}