#2019121000025-LGTD

T112394 分金币

题目描述

圆桌周围坐了\(n\)个人,每个人有一定数量的金币,金币的总数保证是\(n\)的倍数,每个人可以向左或向右给出一些金币,使得最终每个人的金币数相等。你的任务是求出被转移了的金币的总数。

比如,\(n=4\)时,如果四个人的金币数是\(1,2,5,4\),则转移4枚金币(第3个人给第2个人2枚,第2个人和第4个人分别给第1个人1枚金币)即可达到目的。

输入格式

包含多组数据。第一行是测试组数\(T\),接下来每组数据第一行为一个整数\(n\),以下\(n\)行每行为一个整数,按逆时针顺序给出每个人拥有的金币数。

输出格式

对于每组数据,输出被转手金币数量的最小值,输入保证这个值不超过64位无符号整数范围。

输入输出样例

输入

2
3
100
100
100
4
1
2
5
4

输出

0
4

说明/提示

\(1\leq T \leq 10, n\leq 10^6\)

标程时间复杂度:\(O(Tnlogn)\)

题解

对于在座的\(n\)个人,记每个人现在拥有的金币数是\(A_i(i\in [1,n])\),则金币总数\(s=\sum^{n}_{i=1}A_i\),每个人最终得到的金币数$$y=\frac{s}{n}$$
如果1号给2号1枚,而2号给1号2枚,就相当于2号给1号1枚,所以我们用\(x_2\)表示2号给1号了多少金币,特别地,如果\(x_2<0\),实际上是1号给2号\(-x_2\)枚金币。注意,由于是环形,所以\(x_1\)表示1号给\(n\)号了几枚金币。

如果有四个人,依次编号\(1,2,3,4\),对于1号来说,他给了4号\(x_1\)枚金币,但是2号又给了他\(x_2\)枚,我们最终得到1号最终的金币数$$y=A_1-x_1+x_2$$

同理,对于第2个人,有\(y=A_2-x_2+x_3\),我们一共得到了\(n\)个方程,很可惜,我们不能解,因为前\(n-1\)个式子能推导出第\(n\)个,所以只有\(n-1\)个式子是有用的。

尽管这样,我们还是想用\(x_1\)来表示其他的\(x_i\),有:对于第1个人,\(A_1-x_1+x_2=y\),于是$$x_2=y-A_1+x_1=x_1-C_1$$

其中我们规定:$$C_i=A_i-y$$则\(C_1=A_1-y\)

对于第2个人,\(A_2-x_2+x_3=y\),于是$$x_3=y-A_2+x_2=2y-A_1-A_2+x_1=x_1-C_2$$

对于第2个人,\(A_3-x_3+x_4=y\),于是$$x_4=y-A_3+x_3=3y-A_1-A_2-A_3+x_1=x_1-C_3$$

如此推导下去,我们可以得到若干等式。我们的目的是计算所有\(x_i\)的绝对值之和最小,即:$$(|x_1|+|x_1-C_1|+|x_1-C_2|+...+|x_1-C_{n-1}|)_{\min}$$

注意\(|x_i-C_i|\)的几何意义是数轴上点\(x_i\)\(C_i\)的距离,问题变成了:给定数轴上的\(n\)个点,求一个到他们的距离之和尽量小的点。

结论是:这个点是这\(n\)个数的中位数。所以只需排序即可。严谨的证明如下:

在数轴上标出这\(n\)个点,假定数轴上标好了六个点,从左到右编号\(C_i(i\in \{1,2,3,4,5,6\})\),如果选定\(C_4,C_5\)之间的一点\(P\),之后把这个点向左(或者向右,效果相同,假定向左)\(D\)个单位长度,则\(P'\)到它左边的四个点距离减少了\(4D\),到右边的两个点距离增加了\(2D\),总的来说距离减小了\(2D\)

通过这样的移动,发现在奇数个数构成的数列,满足条件的数是最中间的数,而偶数个数构成的数列是最中间两个数的平均数,得证。

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
using namespace std;
const int maxn=1000000+10;
long long A[maxn],C[maxn],tot,M;
int T;
int n;
int main(){
	//freopen("coin10.in","r",stdin); 
	//freopen("coin10.out","w",stdout);
	scanf("%d",&T);
	while(T--){
		scanf("%d",&n);
		tot=0;
		memset(A,0,sizeof(A));
		memset(C,0,sizeof(C));
		for(int i=1;i<=n;i++){
			scanf("%lld",&A[i]);
			tot+=A[i];
		}
		M=tot/n;
		C[0]=0;
		for(int i=1;i<n;i++){
			C[i]=C[i-1]+A[i]-M;
		}
		sort(C,C+n);
		long long x1=C[n/2],ans=0;
		for(int i=0;i<n;i++){
			ans+=abs(x1-C[i]);
		}
		printf("%lld\n",ans);
	}
	return 0;
} 
posted @ 2019-12-10 20:24  刘子闻  阅读(133)  评论(4编辑  收藏  举报