【题目大意】
从$n$颗行星中取走$k$个,每颗行星的质量都为1,记$d_i$为第$i$颗行星到所有行星的重心的距离,问最小的转动惯量$I=sum_{i=1}^{k}w_i*d_i^2$为多少。
【思路分析】
首先我们考虑整理一下求$I$的式子,因为所有的行星质量$w$都为1,所以
$$I=sum_{i=1}^{k}d_i^2$$
我们设重心的位置为$x$,第$i$颗行星的位置为$a_i$,则
$$I=sum_{i=1}^{k}(a_i-x)^2$$
$$I=sum_{i=1}^{k}a_i^2+k*x^2-2*sum_{i=1}^{k}a_i*x$$
我们把行星按照位置坐标从小到大排序,然后记录两个前缀和$s1$和$s2$,其中$s1$记录数组$a$的前缀和,$s2$记录$a^2$的前缀和。
如果要使得$I$最小,那么显然选取一段连续的行星会更优,所以我们可以直接从小到大枚举一段行星中最左边的一个,连续$n-k$个行星的重心就是这$n-k$个行星的坐标的平均数。
【代码实现】
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 #define g() getchar() 7 #define rg register 8 #define go(i,a,b) for(rg int i=a;i<=b;i++) 9 #define back(i,a,b) for(rg int i=a;i>=b;i--) 10 #define db double 11 #define ll long long 12 #define il inline 13 #define pf printf 14 #define db double 15 using namespace std; 16 int fr(){ 17 int w=0,q=1; 18 char ch=g(); 19 while(ch<'0'||ch>'9'){ 20 if(ch=='-') q=-1; 21 ch=g(); 22 } 23 while(ch>='0'&&ch<='9') w=(w<<1)+(w<<3)+ch-'0',ch=g(); 24 return w*q; 25 } 26 const int N=50002; 27 int n,k,T; 28 db a[N],s1[N],s2[N],ans; 29 int main(){ 30 //freopen("","r",stdin); 31 //freopen("","w",stdout); 32 T=fr(); 33 while(T--){ 34 n=fr();k=fr();ans=1e19; 35 go(i,1,n) a[i]=(db)fr(); 36 if(n==k) {pf("0 ");continue;}//一定要特判n=k的情况! 37 sort(a+1,a+1+n); 38 go(i,1,n) s1[i]=s1[i-1]+a[i],s2[i]=s2[i-1]+a[i]*a[i]; 39 rg int m=n-k; 40 go(i,1,k+1){ 41 rg int j=i+m-1; 42 rg db x=1.0*(s1[j]-s1[i-1])/m; 43 ans=min(ans,(db)m*x*x+s2[j]-s2[i-1]-(db)2*(db)(s1[j]-s1[i-1])*x); 44 } 45 pf("%.10lf ",ans); 46 } 47 return 0; 48 }