题意:给定你一条直线,直线上面有n个点,你可以移动k个点,求所有点到重心距离的平方和最小值为多少。

解题思路:这里可以知道 保持一段不移动然后把所有的点都移动到这一段的重心才是最优解,那我们很容易想到枚举这一段的端点,但是如果枚举端点,时间复杂度会高达

n^2,所以我们要知道区间之间的关系,假设 lsum ,rsum  ,lans,rans ,分别为 重心左右的 距离和 和重心左右的平方和,然后每次移动找重心位置,再算 这四个值之间的递推关系就行了。

解题代码:

  1 // File Name: d.cpp
  2 // Author: darkdream
  3 // Created Time: 2014年10月22日 星期三 13时26分25秒
  4 
  5 #include<vector>
  6 #include<list>
  7 #include<map>
  8 #include<set>
  9 #include<deque>
 10 #include<stack>
 11 #include<bitset>
 12 #include<algorithm>
 13 #include<functional>
 14 #include<numeric>
 15 #include<utility>
 16 #include<sstream>
 17 #include<iostream>
 18 #include<iomanip>
 19 #include<cstdio>
 20 #include<cmath>
 21 #include<cstdlib>
 22 #include<cstring>
 23 #include<ctime>
 24 #define LL long long
 25 #define maxn 51005
 26 #define eps 1e-8
 27 using namespace std;
 28 double a[maxn];
 29 int site[maxn];
 30 double zx[maxn];
 31 int LN;
 32 int find(double x , int l , int r)
 33 {
 34 
 35    while(l <= r )
 36    {
 37       int m = (l +  r)/2;
 38       if(a[m] > x)
 39           r = m - 1; 
 40       else 
 41           l = m + 1;
 42    }
 43    return r; 
 44 }
 45 int main(){
 46     int t; 
 47     scanf("%d",&t);
 48     while(t--)
 49     {
 50         int n , k ; 
 51         scanf("%d %d",&n,&k);
 52         for(int i = 1;i <= n;i ++)
 53         {
 54            scanf("%lf",&a[i]);
 55         }
 56         sort(a+1,a+1+n);
 57         if(k >= n)
 58         {
 59             printf("0.00000000000\n");
 60             continue;
 61         }
 62         LN = n - k ;
 63         double sum = 0 ; 
 64         double lans,rans,lsum,rsum;
 65         lans = rans = lsum = rsum = 0 ;
 66 
 67         for(int i = 1;i <= LN;i ++)
 68         {
 69             sum += a[i];  
 70         }
 71         zx[1] = sum / LN;
 72         site[1] = find(zx[1],1,1 + LN-1);
 73         for(int i = 1;i <= LN;i ++)
 74         {
 75            double tt = fabs(zx[1] - a[i]);
 76            if(a[i] <= zx[1])
 77            {
 78               lsum += tt;
 79               lans += tt * tt; 
 80            }else {
 81               rsum += tt; 
 82               rans += tt * tt; 
 83            }
 84         }
 85         double mi = lans + rans;
 86         for(int i = 2;i <= k + 1;i ++)
 87         {
 88            sum -= a[i-1];
 89            sum += a[i + LN -1];
 90            zx[i] = sum/LN;
 91            site[i] = find(zx[i],i,i + LN -1);
 92            double dis = zx[i] - zx[i-1] ;
 93            lsum  -= fabs(a[i-1] - zx[i-1]);
 94            lans  -= fabs(zx[i-1] -a[i-1]) * fabs(zx[i-1] -a[i-1]);
 95            lans  += lsum * dis * 2 + dis *dis * (site[i-1] - i + 1);
 96            lsum += dis * (site[i-1] - i + 1);
 97            for(int j = site[i-1] + 1;j <= site[i];j ++)
 98            {
 99                 double tt = fabs(a[j] - zx[i]);
100                 double tt1 = fabs(a[j] - zx[i-1]);
101                 lsum += tt; 
102                 lans += tt*tt;
103                 rsum -= tt1;
104                 rans -= tt1 * tt1;
105            }
106           // printf("%lf\n",rsum);
107            rans += dis * dis *(i-1 + LN - 1 - site[i]) - rsum * dis * 2;
108            rans += fabs(zx[i] - a[i+LN-1]) * fabs(zx[i] - a[i+LN-1]) ;
109            rsum += fabs(zx[i] - a[i+LN-1]) - dis *(i-1+LN-1-site[i]);
110            //printf("%lf\n",rsum);
111            //printf("%lf %lf %lf %lf %lf\n",lsum,rsum,lans,rans,zx[i]);
112            if(rans + lans < mi)
113                mi = rans + lans;
114         }
115      printf("%.10lf\n",mi);
116         
117     }
118 return 0;
119 }
View Code

 

posted on 2014-10-22 19:28  dark_dream  阅读(298)  评论(0编辑  收藏  举报