蓝桥杯 2024 省赛 A:成绩统计

题目描述

位同学,按照进教室的顺序依次得到编号 ,第 位同学的成绩为 。小蓝依次查看同学成绩:当他看完前 位同学的成绩时,可以在这 个人中任意挑出 名同学,计算他们成绩的方差。请问至少需要查看多少位同学,才有可能找到一组方差严格小于阈值 人集合。如果始终无法满足条件,输出

方差 的定义为

输入格式

  • 第一行:三个整数
  • 第二行: 个整数

输出格式

输出一个整数表示最少需要查看的同学数量;若无法找到满足条件的集合,输出 -1

样例

1
2
3
4
5
6
输入
5 3 1
3 2 5 2 3

输出
4

前三人只能选到 ,方差为约 ;观察到第四人后可以选 ,方差约 ,因此答案为 4。

思路

  • 对答案二分枚举 ,表示“只看前 位同学”是否可行。
  • 对前 个成绩复制并排序,方差最小的 人一定是排序后相邻的 个。
  • 使用前缀和维护滑动窗口内的和与平方和,快速计算每组 个数的方差。
  • 如果存在某组方差不超过阈值,则当前 可行,继续收缩右端;否则扩大搜索范围。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <bits/stdc++.h>
using namespace std;

const int MAXN = 100005;
long long n, k, T;
long long a[MAXN], tmp[MAXN], prefix[MAXN], prefixSq[MAXN];

bool check(long long x) {
for (int i = 1; i <= x; ++i) tmp[i] = a[i];
sort(tmp + 1, tmp + x + 1);
prefix[0] = prefixSq[0] = 0;
for (int i = 1; i <= x; ++i) {
prefix[i] = prefix[i - 1] + tmp[i];
prefixSq[i] = prefixSq[i - 1] + tmp[i] * tmp[i];
}
auto variance = [&](int l, int r) {
long long sum = prefix[r] - prefix[l - 1];
long long sumSq = prefixSq[r] - prefixSq[l - 1];
long double avg = (long double)sum / k;
long double var = (sumSq - 2 * avg * sum + k * avg * avg) / k;
return var;
};
for (int i = k; i <= x; ++i) {
if (variance(i - k + 1, i) <= T) return true;
}
return false;
}

int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);

cin >> n >> k >> T;
for (int i = 1; i <= n; ++i) cin >> a[i];

long long l = k, r = n, ans = -1;
while (l <= r) {
long long mid = (l + r) / 2;
if (check(mid)) {
ans = mid;
r = mid - 1;
} else {
l = mid + 1;
}
}
cout << ans << '\n';
return 0;
}