链接:https://ac.nowcoder.com/acm/contest/881/C
来源:牛客网
Bobo has a point A in the n n n dimension real space R n R_n Rn, whose coodinate is ( a 1 / m , a 2 / m , … , a n / m ) (a_1/m,a_2/m,…,a_n/m) (a1/m,a2/m,…,an/m) where a i a_i ai and m are both integers. He wants to find another point P = ( p 1 , p 2 , … , p n ) P=(p_1,p_2,…,p_n) P=(p1,p2,…,pn) meeting the following requirements.
n
instead of n/1
.输入描述:
The input consists of several test cases and is terminated by end-of-file.
The first line of each test case contains two integers n and m.
The second line contains n n n integers a 1 , a 2 , … , a n a_1,a_2,…,a_n a1,a2,…,an.
输出描述:
For each test case, print a fraction which denotes the result.
输入
1 1
0
2 3
1 2
3 10
1 -2 3
输出
1
0
16/75
题意:
给定 n n n个数字 a 1 a_1 a1到 a n a_n an,要求 n n n个实数 p 1 p_1 p1到 p n p_n pn,使得 ∑ ( a i / m − p i ) 2 \sum(a_i/m−p_i)^2 ∑(ai/m−pi)2最小
并且满足:
p 1 , p 2 , … , p n ≥ 0 p_1,p_2,…,p_n≥0 p1,p2,…,pn≥0
p 1 + p 2 + ⋯ + p n = 1 p_1+p_2+⋯+p_n=1 p1+p2+⋯+pn=1
输出最小的答案,用最简分数表示。
题解:
由于 a i a_i ai互相之间不会影响,所以顺序可以打乱。
将 a i a_i ai从大到小排序,枚举将最大的 i i i个 a i a_i ai处理成同一个值,我们把前i个 a i a_i ai处理成同一个值的时候,假设代价为每个数字和它的初始值的绝对值之差,消耗的代价总和不能超过 m m m。当不能继续放置的时候(设总代价为k),我们就可以将剩余的 m − k m-k m−k的代价均摊到所有非负的 a i a_i ai值上,然后他们对答案分子的贡献就是 ( m − k − s t ∗ v ) 2 (m-k-st*v)^2 (m−k−st∗v)2(st为处理成同一个值的 a i a_i ai个数, v v v那 s t st st个 a i a_i ai处理成的同一个值),剩余的每个 a i a_i ai对答案分子的贡献就是 s t ∗ a [ i ] ∗ a [ i ] st*a[i]*a[i] st∗a[i]∗a[i],分母的值为 s t ∗ m ∗ m st*m*m st∗m∗m
补充一下贪心的正确性证明:
1.
当 g > p / c g>p/c g>p/c时,同样的值 p p p,如果均摊到 c c c个一样的数字 g g g上所得到的答案值为 c ∗ ( g − p / c ) 2 = g 2 ∗ c + p 2 / c − 2 ∗ g ∗ p c*(g-p/c)^2=g^2*c+p^2/c-2*g*p c∗(g−p/c)2=g2∗c+p2/c−2∗g∗p,如果这个值 p p p全都减到一个 g g g上了话,所得到的答案值会是 ( g − p ) 2 + ( c − 1 ) ∗ g 2 = p 2 + g 2 ∗ c (g-p)^2+(c-1)*g^2 = p^2+g^2*c (g−p)2+(c−1)∗g2=p2+g2∗c 由于现在讨论的 g , p , c g,p,c g,p,c均为非负整数,所以 p 2 + g 2 ∗ c > = g 2 ∗ c + p 2 / c − 2 ∗ g ∗ p p^2+g^2*c >= g^2*c+p^2/c-2*g*p p2+g2∗c>=g2∗c+p2/c−2∗g∗p
这个是为什么最后分不完的话要均分成 i i i份的理由
2.
我们知道一件事情,对于一个两个正数 a a a, b b b,还有一个数字 p ( 0 < = p < a , b ) p(0<=p<a,b) p(0<=p<a,b) 如果 a > b a>b a>b那么根据完全平方公式 a 2 − ( a − p ) 2 = 2 ∗ a ∗ p > b 2 − ( b − p ) 2 = 2 ∗ b ∗ p a^2-(a-p)^2=2*a*p > b^2-(b-p)^2=2*b*p a2−(a−p)2=2∗a∗p>b2−(b−p)2=2∗b∗p那么意思就是说这个 p p p如果是被大的数字减去的话,能够将答案减少的量更多,所以我们将这个数字让大的减去。
这个是不断将大的数字变为相同的更小的数字的理由。
3.
我们知道,如果 a i a_i ai是小于0的话,那么它对应的 p i p_i pi如果越大,他对答案的负贡献越大,所以如果 a i a_i ai是小于 0 0 0的话,它对应的 p i p_i pi一律为 0 0 0。除非后面有剩余的 m m m需要分配,才会为了使得所有数字都尽量不要太小才平均分配给每一个数字,那么这时候对于负数的操作就跟正数完全相同了。
#include
#define ll long long
#define pa pair
using namespace std;
ll a[10004];
ll ans,m;
int n;
int w33ha(){
ans=0;
for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
sort(a+1,a+n+1,greater<int>());
ll nd=0;
for(int i=1;i<=n;i++){
if(i<n&&(a[i]-a[i+1])*i+nd<=m){
nd+=(a[i]-a[i+1])*i;
}
else{
ans=(m-nd-i*a[i])*(m-nd-i*a[i]);
for(int j=i+1;j<=n;j++){
ans+=i*a[j]*a[j];
}
ll d,p=i*m*m;
if(ans==0)return puts("0");
d=__gcd(ans,p);
ans/=d;p/=d;
if(p==1)printf("%lld\n",ans);
else printf("%lld/%lld\n",ans,p);
return 0;
}
}
return 0;
}
int main(){
while(scanf("%d%lld",&n,&m)!=EOF)w33ha();
return 0;
}