[CSP-S模拟测试]:平均数(二分答案+归并排序)

题目描述

有一天,小$A$得到了一个长度为$n$的序列。
他把这个序列的所有连续子序列都列了出来,并对每一个子序列都求了其平均值,然后他把这些平均值写在纸上,并对它们进行排序,最后他报出了第$k$小的平均值。
你要做的就是模仿他的过程。


输入格式

第一行两个整数$n,k$,意义如题中所述。
第二行$n$个正整数,即为小$A$得到的序列。


输出格式

一行一个实数,表示第$k$小的平均值,保留到小数点后$4$位。


样例

样例输入:

6 10
3 5 4 6 1 2

样例输出:

3.6667


数据范围与提示

对于$40\%$的数据,$n\leqslant 1,000$。
对于$100\%$的数据,$n\leqslant 100,000,k\leqslant \frac{n\times (n+1)}{2}$,序列中的数$\leqslant {10}^9$。


题解

对于答案,我们可以二分,那么现在我们考虑如何$judge$。

我们可以让整个序列都减去我们现在二分的这个答案,然后对其取前缀和,那么如果前缀和上$S_{l-1}>S_r$的化就说明这段的平均值比二分的答案小;而我们现在要求的就是这个前缀和序列的逆序对的数量,可以用归并排序解决。

精度可能会有问题,建议使用$long\ double$。

扫描二维码关注公众号,回复: 7376565 查看本文章

时间复杂度:$\Theta(n\log^2 n)$。

期望得分:$100$分。

实际得分:$100$分。


代码时刻

#include<bits/stdc++.h>
using namespace std;
const long double eps=1e-5;
int n;
long long k;
long double a[100001],b[100001],que[100001];
int merge(int l,int r)
{
	if(l==r)return 0;
	int mid=(l+r)>>1;
	int res=merge(l,mid)+merge(mid+1,r);
	int lft=l,rht=mid+1,cnt=l-1;
	while(lft<=mid&&rht<=r)
	{
		if(b[rht]<b[lft])
		{
			que[++cnt]=b[rht];
			res+=mid-lft+1;
			rht++;
		}
		else
		{
			que[++cnt]=b[lft];
			lft++;
		}
	}
	while(lft<=mid){que[++cnt]=b[lft];lft++;}
	while(rht<=r){que[++cnt]=b[rht];rht++;}
	for(int i=l;i<=r;i++)b[i]=que[i];
	return res;
}
bool judge(long double x)
{
	for(int i=1;i<=n;i++)
		b[i]=a[i]+b[i-1]-x;
	int res=merge(0,n);
	return res<k;
}
int main()
{
	scanf("%d%lld",&n,&k);
	for(int i=1;i<=n;i++)scanf("%Lf",&a[i]);
	long double lft=0.0,rht=1000000000.0;
	while(rht-lft>=eps)
	{
		long double mid=0.5*(lft+rht);
		if(judge(mid))lft=mid;
		else rht=mid;
	}
	printf("%.4Lf",lft);
	return 0;
}

rp++

猜你喜欢

转载自www.cnblogs.com/wzc521/p/11597864.html