4
糖果传递
AcWing
思路
令 \(x_i\) 表示 \(i\) 给 \(i-1\)(\(i\) 为 \(1\) 时给 \(n\))的糖果数,为负表示 \(i-1\) 给 \(i\) 共 \(-x_i\) 个。考虑求 \(\sum_{i=1}^n|x_i|\)。必须要满足线性方程组:
\[\begin{cases}a_1-x_1+x_2=\bar a\\a_2-x_2+x_3=\bar a\\\dots\\a_n-x_n+x_1=\bar a\end{cases}
\]
整理得
\[\begin{cases}x_1-x_2=a_1-\bar a\\x_2-x_3=a_2-\bar a\\\dots\\x_n-x_1=a_n-\bar a\end{cases}
\]
对所有方程作后缀和得
\[\begin{cases}
x_1=x_1-0\\x_2=x_1-(n-1)\bar a+\sum_{i=2}^na_i\\\dots\\x_{n-1}=x_1-2\bar a+(a_{n-1}+a_n)\\x_n=x_1-\bar a+a_n\end{cases}\]
求它们的绝对值之和的最小值,就是求一个点到 \(0,(n-1)\bar a-\sum_{i=2}^na_i,2\bar a-(a_{n-1}+a_n),\bar a-a_n\),应取中位数为 \(x_1\)。可以用 nth_element
优化到 \(O(n)\)。
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const int N=1000010;
long long n;
typedef long long ll;
ll sum,res,a[N];
int main(){
scanf("%lld",&n);
for(int i=1;i<=n;++i)scanf("%lld",a+i),sum+=a[i];
sum/=n;
for(int i=n;i>1;--i)a[i]+=a[i+1]-sum;
a[1]=0;
nth_element(a+1,a+(n+1>>1),a+1+n);
for(int i=1;i<=n;++i)res+=abs(a[i]-a[n+1>>1]);
printf("%lld",res);
return 0;
}