Robert 的军队
题目大意
给你一个数组,要你选 l~r 个数,使得这些数的方差最小。
思路
首先我们不难想到把人丢去排序,然后因为方差嘛,我们肯定选的是一段连续的区间。
我们要搞出一个结论,就是如果你选好了一个区间,你在旁边再选一个数扩大它,它的方差不会减小,只会不变或变大。
那我们就可以得出一个结论,就是最优你选的数的长度一定是
l
l
l。
然后就很简单了,枚举每个位置,找到对应长度为
l
l
l 的,然后算。
当然不能一个一个枚举,肯定是要化一下式子加速算方差的过程的。
(
x
‾
=
∑
i
=
1
n
a
i
n
\overline{x}=\frac{\sum\limits_{i=1}^na_i}{n}
x=ni=1∑nai)
s
2
=
∑
i
=
1
n
(
a
i
−
x
‾
)
2
n
s^2=\dfrac{\sum\limits_{i=1}^n(a_i-\overline{x})^2}{n}
s2=ni=1∑n(ai−x)2
=
∑
i
=
1
n
(
a
i
2
−
2
a
i
x
‾
)
+
n
x
‾
2
n
=\dfrac{\sum\limits_{i=1}^n(a_i^2-2a_i\overline{x})+n\overline{x}^2}{n}
=ni=1∑n(ai2−2aix)+nx2
=
∑
i
=
1
n
a
i
2
−
2
n
x
‾
2
+
n
x
‾
2
n
=\dfrac{\sum\limits_{i=1}^na_i^2-2n\overline{x}^2+n\overline{x}^2}{n}
=ni=1∑nai2−2nx2+nx2
=
∑
i
=
1
n
a
i
2
−
n
x
‾
2
n
=\dfrac{\sum\limits_{i=1}^na_i^2-n\overline{x}^2}{n}
=ni=1∑nai2−nx2
=
∑
i
=
1
n
a
i
2
n
−
x
‾
2
=\dfrac{\sum\limits_{i=1}^na_i^2}{n}-\overline{x}^2
=ni=1∑nai2−x2
=
∑
i
=
1
n
a
i
2
n
−
(
∑
i
=
1
n
a
i
)
2
n
2
=\dfrac{\sum\limits_{i=1}^na_i^2}{n}-\dfrac{(\sum\limits_{i=1}^na_i)^2}{n^2}
=ni=1∑nai2−n2(i=1∑nai)2
然后每次你往右移,新多的一个数,少了一个数,可以直接快四维护左边上面部分,然后右边上面部分就是直接预处理前缀和然后就可以直接得到。
然后除了下面减了就可以得到了。
代码
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const int N = 100005;
int n, l, r, h[N];
double ans, x, y, sum[N];
int main() {
// freopen("army.in", "r", stdin);
// freopen("army.out", "w", stdout);
scanf("%d %d %d", &n, &l, &r);
for (int i = 1; i <= n; i++) scanf("%d", &h[i]);
sort(h + 1, h + n + 1);
for (int i = 1; i <= n; i++) sum[i] = sum[i - 1] + h[i];
ans = 100000000000000000000000000.0;
for (int i = 1; i <= n; i++) {
int j = max(1, i - l + 1);
x += 1.0 * h[i] * h[i];
if (i >= l) {
y = 1.0 * (sum[i] - sum[j - 1]) * (sum[i] - sum[j - 1]);//维护方差的两个值
ans = min(ans, 1.0 * x / (1.0 * (i - j + 1)) - 1.0 * y / (1.0 * (i - j + 1)) / (1.0 * (i - j + 1)));//求出方差,取最小的
x -= 1.0 * h[j] * h[j];
}
}
printf("%.3lf", ans);
fclose(stdin);
fclose(stdout);
return 0;
}