每日一题:成绩统计
来源:2024蓝桥杯省赛大学A组
题解
首先观察数据范围,注意到数据的量级是1e5,所以纯暴力枚举所有答案的做法肯定是过不了的,需要进行优化。
题干中出现“至少要检查多少个人成绩”,换言之就是再问最小值。那么当题目问的是最小/最大值时,我们就可以思考一下二分的做法能不能解出这道题了,要是解不出来我们就接着思考其他方法。
那么对于这道题的二分做法:对[k, n]的范围,二分答案“检查同学数mid”,判断mid是否满足题目的要求。那么这部分的二分代码就好实现了。
int l = k, r = n;
int ans = INF;
while (l <= r)
{
int mid = l + ((r - l) >> 1);
if (check(mid))
{
ans = mid;
r = mid - 1;
}
else
l = mid + 1;
}
if (ans == INF) //没有找到答案
cout << -1 << '\n';
else
cout << ans << '\n';
那么接下来的关键就是check()的实现,接下来首先需要运用一些数学知识对给出的方差公式进行展开。
\[\sum_{i=1}^{k}{(v_i-\overline{v})^2}=\sum_{i=1}^{k}{v_i^2}-2\overline{v}\sum_{i=1}^{k}{v_i}+k\overline{v}^2
\]
但是对每个check函数如果枚举计算每一项,时间复杂度仍然会很高,所以我们可以用前缀和提前处理数据,这样在每次计算时就是O(1)的时间复杂度。
vector<int> ssum(x + 1, 0), spf(x + 1, 0); // v_i的前缀和与平方的前缀和
for (int i = 1; i <= x; i++)
{
ssum[i] = ssum[i - 1] + b[i];
spf[i] = spf[i - 1] + b[i] * b[i];
}
首先对[1, mid]范围进行排序,就减小了连续的数之间大小的差异,使得连续的k个数方差最小。再通过计算[1, mid]范围内,所有连续的k个数的方差是否小于1,check()返回True/False。
完整代码
//代码来源byboyou,反对直接复制粘贴题解抄袭的行为
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int INF = 0x3f3f3f3f3f3f3f3f;
int n, k, t;
vector<int> a;
bool check(int x)
{
vector<int> b(x + 1);
for (int i = 1; i <= x; i++)
b[i] = a[i];
sort(b.begin() + 1, b.end());
vector<int> ssum(x + 1, 0), spf(x + 1, 0); // 前缀和与平方的前缀和
for (int i = 1; i <= x; i++)
{
ssum[i] = ssum[i - 1] + b[i];
spf[i] = spf[i - 1] + b[i] * b[i];
}
double avg = 0; // v的均值
for (int i = 1; i <= k; i++)
avg += b[i] / (double)k;
double var = (spf[k] - 2 * avg * ssum[k] + k * avg * avg) / (double)k;
if (var < t)
return 1;
for (int i = k + 1; i <= x; i++)
{
avg = avg - b[i - k] / (double)k + b[i] / (double)k;
var = ((spf[i] - spf[i - k]) - 2 * avg * (ssum[i] - ssum[i - k]) + k * avg * avg) / (double)k;
if (var < t)
return 1;
}
return 0;
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n >> k >> t;
a.resize(n + 1);
for (int i = 1; i <= n; i++)
{
cin >> a[i];
}
int l = k, r = n;
int ans = INF;
while (l <= r)
{
int mid = l + ((r - l) >> 1);
if (check(mid))
{
ans = mid;
r = mid - 1;
}
else
l = mid + 1;
}
if (ans == INF)
cout << -1 << '\n';
else
cout << ans << '\n';
return 0;
}
浙公网安备 33010602011771号