HDU 5073 Galaxy(2014鞍山赛区现场赛D题)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5073

解题报告:在一条直线上有n颗星星,一开始这n颗星星绕着重心转,现在我们可以把其中的任意k颗星星移动到这条直线上的任意位置然后围绕这个新的重心转,   这个星系的惯性值I = w1 * d1^2 + w2 * d2 ^ 2 +......+wn * dn ^ 2,其中wi表示第i颗星星的重量,di表示第i颗星星到这个星系的重心的距离.然后现在要你求转移k颗星星之后惯性值最小是多少?

n颗星星转移k颗后还剩下n-k颗是不能移动的,很显然转移的k颗星星一定是转移到重心的位置,然后每颗星星的重量都是1,所以求I时就可以不用管wi了,然后我们可以枚举n-k个星星所在的区间,而且可以确定这n-1个星星在位置上一定是连续的.然后枚举出了k个区间,怎么能在O(1)时间算出每个区间的I值呢?作如下转化(其中e表示重心的坐标):

 (x1-e)^2 + (x2-e)^2 (x3-e)^2+....(xn-e)^2

=x1^2 + x2^2+...xn^2 + n * e^2 - 2*e*(x1+x2+x3+...xn)

这样通过预先求出x1的平方到xn的平方的和还有x1到xn的和就可以在O(1)时间内求出I的值了.

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #include<iostream>
 5 #include<cmath>
 6 using namespace std;
 7 const int maxn = 50050;
 8 const double eps = 1e-14;
 9 
10 int T,n,k;
11 double pos[maxn],sump[maxn],sump2[maxn];
12 int main()
13 {
14   //  freopen("in","r",stdin);
15     scanf("%d",&T);
16     while(T--)
17     {
18         scanf("%d%d",&n,&k);
19         for(int i = 1;i <= n;++i)
20         scanf("%lf",&pos[i]);
21         sort(pos+1,pos+n+1);
22         sump[0] = sump[1] = sump2[0] = sump2[1] = 0;
23         for(int i = 1;i <= n;++i)
24         {
25             sump[i] = sump[i-1] + pos[i];
26             sump2[i] = sump2[i-1] + pos[i] * pos[i];
27         }
28         int m = n - k;
29         if(m  <= 1)
30         {
31             puts("0");
32             continue;
33         }
34         double ans = sump2[m] + m * (sump[m] / m) * (sump[m] / m) - 2.0 * (sump[m] / m) * sump[m];
35         for(int e = m;e <= n;++e)
36         {
37             int s = e - m + 1;
38             double eve = (sump[e] - sump[s-1]) / m;
39             double temp = sump2[e] - sump2[s-1] + m * eve * eve - 2.0 * eve * (sump[e] - sump[s-1]);
40             ans = min(ans,temp);
41         }
42         printf("%.12lf\n",ans);
43     }
44     return 0;
45 }
View Code

 

posted @ 2014-11-12 10:33  xiaxiaosheng  阅读(171)  评论(0编辑  收藏  举报