题意:
数轴上有n个点 每个点重量1 可以移动其中k个到任何位置 使得题中式子值最小 di表示第i个点距离现在n个点的重心的距离
思路:
式子中wi可以去掉 因为都是1 则 式子变成I=min(sum(di*di))
考虑移动的k个点 应该直接把它们移到重心 这样di为0
很容易想到 我们将所有点排序后 应该从两边往中间拿 这样移动k个点 剩下一些连续的点 因此可以枚举剩下的连续区间
此时我们把I变形 I = min(sum(di*di))
= min(sum((li-mi)*(li-mi))) (li即i点的位置 mi为这几个点的重心)
= min( sum(li*li) - 2*mi*sum(li) + mi*mi*(n-k) )
这样我们可以用前缀和来维护答案 结合上面的枚举 复杂度也就是O(n)
代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<set>
#include<vector>
#include<queue>
#include<cstdlib>
#include<ctime>
#include<cmath>
#include<bitset>
using namespace std;
typedef long long LL;
#define M 50010
#define mod 1000000007
int T, n, k;
double s[M], s2[M];
double ans;
int main() {
int i, j;
double tmp, mid;
scanf("%d", &T);
while (T--) {
scanf("%d%d", &n, &k);
for (i = 1; i <= n; i++)
scanf("%lf", &s[i]);
ans = 0;
if (n != k) {
sort(s + 1, s + n + 1);
for (i = 1; i <= n; i++)
s2[i] = s[i] * s[i];
double sum1 = 0, sum2 = 0;
for (i = 1; i <= n - k; i++) {
sum1 += s[i];
sum2 += s2[i];
}
mid = sum1 / (n - k);
ans = sum2 - 2.0 * mid * sum1 + mid * mid * (n - k);
for (i = 2, j = n - k + 1; j <= n; i++, j++) {
sum1 -= s[i - 1];
sum1 += s[j];
sum2 -= s2[i - 1];
sum2 += s2[j];
mid = sum1 / (n - k);
tmp = sum2 - 2.0 * mid * sum1 + mid * mid * (n - k);
if (tmp < ans)
ans = tmp;
}
}
printf("%.10f\n", ans);
}
return 0;
}