思路
看到至少需要多少人,且根据题意得出,人数越多越容易满足条件,所以可以二分求左边界即最小值得到结果。
要判断从前x个人选出k人的方差是否能小于t,即找到方差的最小值与t比较即可,直接找最小值比较难,但是我们可以通过排序,求出最小的几组方差都与t比较即可。这几组方差的计算可以枚举区间的左端点,但是如果要直接求的话,需要算出这组数据的平均值,时间复杂度较高,但可以通过数学推导转化成前缀和,只需O(1)复杂度。
方差转化过程
#include <bits/stdc++.h>
using namespace std;
typedef __int128 ll;
const int N=1e5+10;
int aa[N];
ll a[N],b[N],pre[N],pre2[N];
ll n,k,t; //要开成int128 ,因为k*k*t会爆long long
bool check(int x)
{
for(int i=1;i<=x;i++) b[i]=a[i]; //将a赋给b,对b排序提高鲁棒性
sort(b+1,b+1+x); //将b排序是为了求出最小的几组方差
//求前缀和
for(int i=1;i<=x;i++){
pre[i]=pre[i-1]+b[i];
pre2[i]=pre2[i-1]+b[i]*b[i];
}
//枚举x人的区间的左端点
for(int i=1;i+k-1<=x;i++)
{
ll s=k*(pre2[i+k-1]-pre2[i-1])-(pre[i+k-1]-pre[i-1])*(pre[i+k-1]-pre[i-1]); //[i,i+k-1]区间的和
if(s<k*k*t) return 1;
}
return 0;
}
int main()
{
int n1,k1,t1;
cin>>n1>>k1>>t1; //由于int128只能进行赋值和四则运算,所以要用int过渡
n=n1,k=k1,t=t1;
for(int i=1;i<=n;i++) cin>>aa[i],a[i]=aa[i]; //由于int128只能进行赋值和四则运算,所以要用int过渡
//二分答案 ,找最小的左端点
int l=1,r=n1;
while(l<r)
{
int mid=l+r>>1;
if(check(mid)) r=mid;
else l=mid+1;
}
if(check(l)) cout<<l;
else cout<<-1;
return 0;
}