P5817 [CQOI2011] 分金币

· · 题解

目标是让每个人的金币数变为 \large\overline{a}=\frac{\sum\limits_{i=1}^n a_i}{n}

X_i 为编号为 i 的人向其左边的人给的金币数量,负数反之。则 ans=\large\sum\limits_{i=1}^n |X_i|

我们发现传递后第 i 个人金币数为 a_i+X_{i+1}-X_i=\overline{a}

所以 X_i=X_{i-1}+\overline{a}-a_{i-1}

X_2=X_1+\overline{a}-a_1

易得 $X_i=X_1+(i-1)\times\overline{a}-\large\sum\limits_{j=1}^{i-1}a_j$。 记 $C_i=\large\sum\limits_{j=1}^i a_j-i\times\overline{a}=\large\sum\limits_{j=1}^i(a_j-\overline{a})$, 则 $X_i=X_1-C_{i-1}$,$C$ 数组可求前缀和得出。 则 $ans=\large\sum\limits_{i=1}^n|X_1-C_{i-1}|$,类似在数轴上有 $n$ 个点,找到一个与所有点距离和最小的点。对 $C$ 数组排序,若 $n$ 为奇数,$X_i$ 取 $\large C_{\frac{n+1}{2}}$;若 $n$ 为偶数,$X_1$ 取 $\large C_{\frac{n}{2}-1}$ 到 $\large C_{\frac{n}{2}}$ 的数都可以,所以我们取 $\large X_1=C_{\lfloor{\frac{n+1}{2}}\rfloor}$ 便可以求出 $ans$ 的最小值。 时间复杂度 $\large\mathcal{O}(n \log n)$,瓶颈在于排序。 ## Code ``` #include<bits/stdc++.h> using namespace std; const int N=1e5+1; int n,a[N],sum,mid,s[N]; long long ans; int main(){ scanf("%d",&n); for(int i=1;i<=n;++i){ scanf("%d",a+i); sum+=a[i]; } sum/=n; for(int i=1;i<n;++i) s[i]=s[i-1]+a[i]-sum; sort(s,s+n); mid=s[n>>1]; for(int i=0;i<n;++i) ans+=abs(s[i]-mid); printf("%lld\n",ans); return 0; } ```