2019牛客暑期多校训练营(第一场) C Euclidean Distance

博客探讨了如何在高维空间中找到一个点P,使其与已知点A之间的欧几里得距离平方最小化。点P的坐标必须非负且和为1。文章提供了一个涉及通分和优化策略的解决方案,并通过举例说明了如何通过调整点P的坐标来最小化距离。此外,还提及了证明该最小距离总是有理数的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Bobo has a point A in the n dimension real space RnRn, whose coodinate is (a1/m,a2/m,…,an/m)(a1/m,a2/m,…,an/m) where aiai and m are both integers. He wants to find another point P=(p1,p2,…,pn)P=(p1,p2,…,pn) meeting the following requirements.

* p1,p2,…,pn∈Rp1,p2,…,pn∈R. That is, they are real numbers.
* p1,p2,…,pn≥0p1,p2,…,pn≥0
* p1+p2+⋯+pn=1p1+p2+⋯+pn=1
* The (squared) Euclidean distance between P and A, which is ∥A−P∥22=∑ni=1(ai/m−pi)2‖A−P‖22=∑i=1n(ai/m−pi)2, is minimized.

It can be proved the minimum is always a rational number. Print the squared distance in fraction. Note to print an integer n as `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 integers a1,a2,…,ana1,a2,…,an.

* 1≤n≤1041≤n≤104
* 1≤m≤1031≤m≤103
* −m≤ai≤m−m≤ai≤m
* The sum of n does not exceed 5×1055×105.

输出描述:

For each test case, print a fraction which denotes the result.

示例1

输入

复制

1 1
0
2 3
1 2
3 10
1 -2 3

输出

复制

1
0
16/75

传送门
题意:给定n个数字a1​到an​,要求n个实数p1​到pn​,使得\sum_{i=1}^{n}(ai/m-pi)^{2}最小
并且满足:
p1​,p2​,…,pn​≥0
p1​+p2​+⋯+pn​=1
输出最小的答案,用最简分数表示。


思路:首先进行通分 变成 \sum_{i=1}^{n}\frac{(ai-m*pi)^{2}}{m^{2}} 那么此处的p_i扩大为题目给的m倍,要想分子最小,那么p_i​只能将a_i​的值不断减小而不能增加(即使ai​<0),因此我们就可以通过调节p_i的值使得a_i​的最大值尽可能的小),因此我们就可以通过调节p_i​的值使得a_i​的最大值尽可能的小,我们就m*p_i看成一个整体 那么总共减少值(ans)为m。

假设我们进行处理前i-1后p_i的和还剩下ans,前i个的a的值都已经被削到了a_i,那么:

  • 如果  i*(a[i]-a[i-1])<=ans,那么ans=ans-i*(a[i]-a[i-1]);// 下证 a数组从大到小排序
  • 否则,就记录这个位置为indx,并且break(indx的初始值为n)。

其中的idnx就是说我们可以通过调节pi​的值使得前idnx个数都相等且等于a_{indx}-\frac{ans}{indx},因此最后答案是​​

\frac{indx*(a(indx)-\frac{ans}{indx})^{2}+\sum_{i=indx+1}^{n} a(indx)^2 }{m^2}

举个例子

首先我们将aa数组排序得到:
    3     1     −2

  • 处理前1个数,此时ans=10>1*(3-1)=2,于是我们将a1​变成1,ans消耗2;
  • 处理前2个数,此时ans=8>2*(1-(-2))=6,于是我们将a1,a2​变成-2,ans消耗6;
  • 处理前3个数,此时ans=2,a1=a2=a3=-2,由于ans最后要变成0且ai的最大值要竟可能小,那么我们均匀分配,所以a1=a2=a3=-2-\frac{2}{3}=-\frac{8}{3}

所以最后答案为\frac{(-\frac{8}{3})^2*3}{10*10}=\frac{16}{75}

证明

 在此感谢大佬 https://blog.nowcoder.net/n/77aaf4ba0b8f477ba2613da5a7dcd2be

#include<cstring>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
int cmp(int x,int y)
{
	return x>y;
}
int main()
{
	ll n,m;
	while(cin>>n>>m)
	{
		ll a[10100];
		for(int i=1;i<=n;i++)
		cin>>a[i];
		sort(a+1,a+1+n,cmp);
		ll ans=m;
		ll indx=n;
		for(int i=1;i<n;i++)
		{
			if(i*(a[i]-a[i+1])>=ans)
			{
				indx=i;
				break;
			}
			else
			ans-=i*(a[i]-a[i+1]);
		}
		ll sum=1ll*(indx*a[indx]-ans)*(indx*a[indx]-ans);
		for(int i=indx+1;i<=n;i++)
		{
			sum+=1ll*a[i]*a[i]*indx;
		}
		ll x=m*m*indx;
		ll tmp=__gcd(x,sum);
		sum/=tmp,x/=tmp;
		if(x==1)
		{
			cout<<sum<<endl;
		}
		else
		cout<<sum<<"/"<<x<<endl;
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值