非常好的一道数学题。
题目分析
(参考刘汝佳《算法竞赛入门经典 ⋅ \cdot ⋅ 训练指南》)
本身看起来很复杂。不要急,我们慢慢分析。
首先,每个人最终的金币数是可以计算出来的,即总金币数去除以总人数,我们设这个数等于 M M M。
假设一共有 n n n 个人,编号为 1 1 1, 2 2 2, 3 3 3, 4 4 4, ⋯ \cdots ⋯。设每个人刚开始拥有的金币数为 A i A_i Ai, x i x_i xi 表示第 i i i 个人给其上一个人的金币数( x 1 x_1 x1 表示给 n n n 的金币数)。那么对于编号 1 1 1,经转手后的金币数就等于 M = A 1 − x 1 + x 2 M=A_1-x_1+x_2 M=A1−x1+x2,同理: M = A 2 − x 2 + x 3 , M = A 3 − x 3 + x 4 ⋯ M=A_2-x_2+x_3,M=A_3-x_3+x_4\cdots M=A2−x2+x3,M=A3−x3+x4⋯。最终我们就可以获得第 n n n 个式子 M = A n − x n + x 1 M=A_n-x_n+x_1 M=An−xn+x1,那我们是不是就可以解方程组了呢?很遗憾,最后一个方程是可以通过前 n − 1 n-1 n−1 个方程推导出来的,故只有前 n − 1 n-1 n−1 个方程是有用的。
尽管无法直接求解,我们还是可以用 x 1 x_1 x1 表示其他的 x i x_i xi,进而转化为单变量的极值问题。
对于第 1 1 1 个人,可以得到 x 2 = x 1 + M − A 1 x_2=x_1+M-A_1 x2=x1+M−A1。
对于第 2 2 2 个人,可以得到 x 3 = x 2 + M − A 2 = x 1 + M − A 2 + M − A 1 x_3=x_2+M-A_2=x_1+M-A_2+M-A_1 x3=x2+M−A2=x1+M−A2+M−A1。
对于第 3 3 3 个人,可以得到 x 4 = x 3 + M − A 3 = x 1 + M − A 3 + M − A 2 + M − A 1 x_4=x_3+M-A_3=x_1+M-A_3+M-A_2+M-A_1 x4=x3+M−A3=x1+M−A3+M−A2+M−A1。
⋯ \cdots ⋯
所以对于第 n n n 个人,有 x n = x 1 + n M − ( ∑ i = 1 n A i ) x_n=x_1+nM-(\sum_{i=1}^nA_i) xn=x1+nM−(∑i=1nAi)。
最后,我们要求解的是转手金币数的最小值,也就是 min { ∑ i = 1 n ∣ x i ∣ } \min\{\sum_{i=1}^n|x_i|\} min{∑i=1n∣xi∣},所以这个时候我们令 C i = ( ∑ i = 1 n A i ) − i ⋅ M C_i=(\sum_{i=1}^nA_i)-i\cdot M Ci=(∑i=1nAi)−i⋅M,这样,我们要求解的式子就变成 min { ∣ x 1 ∣ + ∣ x 1 − C 1 ∣ + ∣ x 2 − C 2 ∣ + ⋯ + ∣ x n − C n ∣ } \min\{|x_1|+|x_1-C_1|+|x_2-C_2|+\cdots +|x_n-C_n|\} min{∣x1∣+∣x1−C1∣+∣x2−C2∣+⋯+∣xn−Cn∣}。由于两个值的差的绝对值的几何意义是二者的距离,所以我们把求最小值这个问题放在数轴上来解决,问题也转成求 x 1 x_1 x1 到各个 C i C_i Ci 的距离之和的最小值。
假设我们的红圈( x 1 x_1 x1)向右微微移动了 d d d 个单位,那么对于左边就增加了 2 d 2d 2d 的距离,对于右边减少了 4 d 4d 4d 的距离,故减少了 2 d 2d 2d 的距离。所以每次只要我们向蓝圈更多的一边去移动,就会对答案产生贡献,进而推出当红圈两边的蓝圈数相等,即处于中位数时,可以获得最小值。之后我们将 C i C_i Ci 从小到大来排序,然后统计答案即可。
对于 A i A_i Ai,我们没有任何必要保存,具体实现请看代码:
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#define ll long long
using namespace std;
ll M,sum;
int n;
ll C[1000005];
inline ll re(){
register ll k=0,f=1ll;
register char c=getchar();
while(!isdigit(c)){
if(c=='-') f=-1ll;
c=getchar();
}
while(isdigit(c)){
k=k*10ll+(c^48ll);
c=getchar();
}
return 1ll*k*f;
}
void wr(ll x){
if(x<0){
x=~x+1;
putchar('-');
}
if(x>9) wr(x/10ll);
putchar(x%10ll^48ll);
}
signed main(){
while(scanf("%d",&n)!=EOF){
for(int i=1;i<=n;++i) C[i]=0;
for(int i=1;i<=n;++i){
ll x=re();
C[i]=x+C[i-1];
}
M=C[n]/n;
for(int i=1;i<=n;++i) C[i]-=i*M;
sort(C+1,C+1+n);
ll x=C[(n+1)/2],ans=0;
for(int i=1;i<=n;++i)
ans+=abs(x-C[i]);
wr(ans);
putchar('\n');
}
return 0;
}