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;
}
posted @ 2023-02-23 22:01  wscqwq  阅读(7)  评论(0编辑  收藏  举报