题目描述
有 n n n 位同学,按照进教室的顺序依次得到编号 1 … n 1 \ldots n 1 … n ,第 i i i 位同学的成绩为 a i a_i a i 。小蓝依次查看同学成绩:当他看完前 x x x 位同学的成绩时,可以在这 x x x 个人中任意挑出 k k k 名同学,计算他们成绩的方差。请问至少需要查看多少位同学,才有可能找到一组方差严格小于阈值 T T T 的 k k k 人集合。如果始终无法满足条件,输出 − 1 -1 − 1 。
方差 σ 2 \sigma^2 σ 2 的定义为
σ 2 = 1 k ∑ i = 1 k ( v i − v ˉ ) 2 , v ˉ = 1 k ∑ i = 1 k v i . \sigma^2 = \frac{1}{k}\sum_{i=1}^{k}(v_i-\bar v)^2,\quad
\bar v = \frac{1}{k}\sum_{i=1}^{k}v_i.
σ 2 = k 1 i = 1 ∑ k ( v i − v ˉ ) 2 , v ˉ = k 1 i = 1 ∑ k v i .
输入格式
第一行:三个整数 n , k , T n,k,T n , k , T 。
第二行:n n n 个整数 a 1 , a 2 , … , a n a_1,a_2,\ldots,a_n a 1 , a 2 , … , a n 。
输出格式
输出一个整数表示最少需要查看的同学数量;若无法找到满足条件的集合,输出 -1。
样例
1 2 3 4 5 6 输入 5 3 1 3 2 5 2 3 输出 4
前三人只能选到 { 3 , 2 , 5 } \{3,2,5\} { 3 , 2 , 5 } ,方差为约 1.56 1.56 1 . 5 6 ;观察到第四人后可以选 { 3 , 2 , 2 } \{3,2,2\} { 3 , 2 , 2 } ,方差约 0.22 < 1 0.22 < 1 0 . 2 2 < 1 ,因此答案为 4。
思路
对答案二分枚举 x x x ,表示“只看前 x x x 位同学”是否可行。
对前 x x x 个成绩复制并排序,方差最小的 k k k 人一定是排序后相邻的 k k k 个。
使用前缀和维护滑动窗口内的和与平方和,快速计算每组 k k k 个数的方差。
如果存在某组方差不超过阈值,则当前 x x x 可行,继续收缩右端;否则扩大搜索范围。
代码
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 ; }