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
说明/提示
(1leq T leq 10, nleq 10^6)
标程时间复杂度:(O(Tnlogn))
题解
对于在座的(n)个人,记每个人现在拥有的金币数是(A_i(iin [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(iin {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
",ans);
}
return 0;
}