2019牛客暑期多校訓練營(第一場)----C-Euclidean Distance

首先發出題目鏈接:
鏈接:https://ac.nowcoder.com/acm/contest/881/C
來源:牛客網
涉及:數學,gcd

點擊這裏回到2019牛客暑期多校訓練營解題—目錄貼


題目如下:
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
如題所示,題目想要得到那個最短距離的值,而不是P點的座標,注意答案如果是整數就輸出整數,否則輸出分數


設距離爲X,則
Xmin=(a1p1)min2+(a2p2)min2+...+(anpn)min2X_{min}=(a_1-p_1)^2_{min}+(a_2-p_2)^2_{min}+...+(a_n-p_n)^2_{min}
故需要求得(a1p1),(a2p2),...,(anpn)(a_1-p_1),(a_2-p_2),...,(a_n-p_n)這一些列數的最小值

且:
 
 1.p1+p2+...+pn=mp_1+p_2+...+p_n=m(注意,因爲我沒有把所有的aia_i除以m,所以所有的p值加起來等於m,最後答案再除以m就ok)
 
 2.由於p1,p2,...,pn0p_1,p_2,...,p_n \ge0(a1p1)a1,(a2p2)a2,...,(anpn)an(a_1-p_1)\le a_1,(a_2-p_2)\le a_2,...,(a_n-p_n)\le a_n


也就是說,題目可以變成,把m的值分配給每一個pip_i,讓每一個aipi\left\vert a_i-p_i\right\vert儘可能的小,先對所有aia_i有大到小排序,如圖所示(n=5):
在這裏插入圖片描述
在分配的時候,我們想讓每一個aia_i都儘量的小,所以我們先將m分配給比較大的aia_i(即排序後的a1a_1

讓比較大的aia_i減小一個數b,其收益比讓比較小的aia_i減去一個數b的收益大,可以證明:
假設a1a_1a2a_2大,由於收益(最小距離)是平方和狀態,則明顯a12(a1b)2>a22(a2b)2a_1^2-(a_1-b)^2>a_2^2-(a_2-b)^2


所以首先我們將m的值分配一些給a1a_1來減小a1a_1減小到和a2a_2一樣大,於是
在這裏插入圖片描述
假設此時發現m分配一些值之後,m仍然沒有等於0,此時還需要繼續分配,但是,此時要將剩下的m的值同時分配給a1a2a_1,a_2,讓它們同時減小。

也可以證明同時減小比其中一個減小的收益高

於是
在這裏插入圖片描述
到了此時,仍然有剩餘的值,那就讓a1,a2,a3a_1,a_2,a_3同時減小,但是發現,三個值減小到0的時候,m還有剩餘的值,此時就需要將a1,a2,a3a_1,a_2,a_3減小到負數了,那麼a1a2a3\left\vert a_1\right\vert \left\vert a_2\right\vert \left\vert a_3\right\vert的值會增大(此時的減小就成爲了虧損),但是由於m的值必須全部分配完畢,所以必須得減小。

也可以證明a1,a2,..,ana_1,a_2,..,a_n都小於0時,讓它們同時減小的虧損比讓某一個減少的虧損小。

假設此時m剩餘的值,已經不足讓a1,a2,a3a_1,a_2,a_3減小到a4a_4那麼大,於是,在減小到某一值就會停止減小
在這裏插入圖片描述
此時,m=0,得到最短距離,距離爲
a12+a22+..+a52m2\frac {a_1^2+a_2^2+..+a_5^2}{m^2}
按照這種思路,不管n爲多少,只要將m分配給所有最大的aia_i,讓他們同時減小,直到m=0,那麼距離就爲a12+a22+..+an2m2\frac {a_1^2+a_2^2+..+a_n^2}{m^2}

sort(a+1,a+n+1,greater<int>());//由大到小排序
for(i=1;i<n;i++){
	int x=a[i]-a[i+1];//每一個a[i]與a[i+1]之間的差值
	if(tot+x*i<=m)	tot+=x*i;//tot是m已經分配的值,tot+x*i用來判斷m還能不能分配值,使a[1],..,a[i]到a[i+1]的位置
	else	break;//不能剛好分配
}
int rem=m-tot; //m剩餘的值
//fz/fm等於前面a[1],...,a[i]最小能減少到多少
ll fz=i*a[i]-rem;
ll fm=m*i;
ll gcd=getgcd(fz*fz*i,fm*fm);
fz=fz*fz*i/gcd;fm=fm*fm/gcd;//(fz*fz*i)/(fm*fm)是前面a[1],...,a[i]對距離的貢獻
for(i=i+1;i<=n;i++)	get(fm,fz,m,a[i]);//加上後面沒有分配到值的a[i+1],..,a[n]對距離的貢獻(a[i]*a[i])/m*m
void get(ll &fm,ll &fz,ll m,ll z){//分數加法函數
	ll gcd=getgcd(fz*m*m+z*z*fm,fm*m*m);
	fz=(fz*m*m+z*z*fm)/gcd;
	fm=fm*m*m/gcd;
	return;
}

當然,排序後分配m,a1,a2,...,an1a_1,a_2,...,a_{n-1}都減小到了ana_n,發現此時m還有剩餘的值,那就將a1,a2,...,ana_1,a_2,...,a_n一起減小
在這裏插入圖片描述
此時剛好a1=a2=...=ani=na_1=a_2=...=a_n,i=n

fz=n*a[n]-rem;
fm=m*n;
ll gcd=getgcd(fz*fz*n,fm*fm);
fz=fz*fz*n/gcd;fm=fm*fm/gcd;	

舉個例子
n=3,m=10,a={1,-2,3}
在這裏插入圖片描述

1.首先將a1a_1減至a2a_2,此時m剩餘8,如圖
在這裏插入圖片描述

2.再將a1,a2a_1,a_2減到a3a_3,此時m剩餘2
在這裏插入圖片描述

3.m還有剩餘,a1,a2,a3a_1,a_2,a_3繼續減小
在這裏插入圖片描述

於是最短距離爲
(83)23102=1675\frac {(-\frac 8{3})^2·3}{10^2}=\frac {16}{75}


代碼如下:

#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
typedef long long ll;
const int maxn=1e4+5;
int n,m,a[maxn];//題目所給變量
ll getgcd(ll a,ll b){return (b==0?a:getgcd(b,a%b));}
void get(ll &fm,ll &fz,ll m,ll z){//分數加法函數
	ll gcd=getgcd(fz*m*m+z*z*fm,fm*m*m);
	fz=(fz*m*m+z*z*fm)/gcd;
	fm=fm*m*m/gcd;
	return;
}
int main(){
	while(~scanf("%d%d",&n,&m)){
		int i,tot=0;
		for(i=1;i<=n;i++)	scanf("%d",&a[i]);
		sort(a+1,a+n+1,greater<int>());//由大到小排序
		for(i=1;i<n;i++){
			int x=a[i]-a[i+1];//每一個a[i]與a[i+1]之間的差值
			if(tot+x*i<=m)	tot+=x*i;//tot是m已經分配的值,tot+x*i用來判斷m還能不能分配值,使a[1],..,a[i]到a[i+1]的位置
			else	break;//不能剛好分配
		}
		int rem=m-tot;//m剩餘的值
		//fz/fm等於前面a[1],...,a[i]最小能減少到多少
		ll fz=i*a[i]-rem;
		ll fm=m*i;
		ll gcd=getgcd(fz*fz*i,fm*fm);
		fz=fz*fz*i/gcd;fm=fm*fm/gcd;//約分
		//(fz*fz*i)/(fm*fm)是前面a[1],...,a[i]對距離的貢獻
		for(i=i+1;i<=n;i++)	get(fm,fz,m,a[i]);//加上後面沒有分配到值的a[i+1],..,a[n]對距離的貢獻(a[i]*a[i])/m*m
		//下面是輸出限制
		if(fm==1)	printf("%lld\n",fz);
		else	printf("%lld/%lld\n",fz,fm);
	}
	return 0;
} 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章