[HAOI2008]糖果传递

[HAOI2008]糖果传递

Description

\(n\)个小朋友坐成一圈,每人有\(a_i\)个糖果。每人只能给左右两人传递糖果。每人每次传递一个糖果代价为\(1\)

Input

第一行一个正整数\(n<=1000000\),表示小朋友的个数.

接下来\(n\)行,每行一个整数\(a_i\),表示第i个小朋友得到的糖果的颗数.

Output

求使所有人获得均等糖果的最小代价。

Sample Input

4
1
2
5
4

Sample Output

4

环形均分纸牌

本来以为均分纸牌很简单,才发现很多东西理解不到位,这里直接转载大佬的题解啦。

原题解戳这里

首先这道题是一个模型,我们称之为环形均分纸牌。显然这个模型是出自luogu1031那道均分纸牌,没有过的可以先过一下。

先来看不是环形的情况。

假设总和为 \(T\) ,有 \(M\) 张纸牌。设 \(ave = \dfrac{T}{M}\) 。对于第\(i\)个人来说,如果 \(C[i] < ave\) 他拿的应该是 \(C[i] + ave\) 张,后一个拿 \(C[i + 1] - ave + C[i]\) ,否则,他拿 \(C[i] - ave\) 张,而后一个拿 \(C[i + 1] + C[i] - ave\) 张。

但是这样并不方便维护,我们考虑整体和隔离的思想。将前\(i\)个看做一个整体,显然前\(i\)个内部的均分是不会改变其整体结构的,因而对于该体系来说,想要达到平均数结构,就必须与下一个体系交换足够的纸牌,而交换数量就是 \(|G[i] - i \cdot ave|\) ,其中 \(G[i]\) 是前缀和。然后就可以推出一个结论: \(d = \sum ^M _{i = 1} |i \cdot ave - G[i]|\),也就是将每次体系更新的贡献加起来。

如果让每个人的数量都减去 \(ave\) ,结果就可以经过简单的数学推导进一步化简: \(d = \sum ^M _{i = 1} |S[i]|\) ,其中 \(S[i]\) 是新数组的前缀和。这就是均分纸牌问题的通用公式。

现在考虑一种变形:如果这里的纸牌是环形的呢?

对于环形问题,首先考虑切开。假定我们切开的东西是 \(A[k + 1], A[k + 2], ..., A[M], A[1], ..., A[k]\) ,那么其前缀和也会有所变化,即 \(S[k + 1] - S[k], S[k + 2] - S[k], ..., S[M] - S[k], .S[1] + S[M] - S[k], ..., S[M]\)

由于均分之后, \(S[M] = 0\)恒成立,所以前缀和的变化仅仅是减去 \(S[k]\) 。那么,我们要求的就是哪个取值上最短,换言之,求什么时候 \(\sum^M_{i = 1} |S[i] - S[k]|\) 取到最小。

因而,这里我们要求的东西是 \(\sum^M_{i = 1} |S[i] - S[k]|\) 的最小值。答案是在中位数处取到,原因各位可以想象将 \(S[i]\) 投影到一个坐标平面内。然后我们用一条线去扫,点到线的距离之和就是上面的式子的最小值。从中位数的位置变化到靠下的位置或是靠上的位置,都会使某一部分点的距离增大。所以这里转化为求中位数,也就是求第 \(\dfrac{n + 1}{2}\) 大元素,由于 \(N \leq 10^6\) 所以不建议排序(虽然也能用)。我们可以用\(STL\)的nth_elements()函数\(O(n)\)求出\(k\)大。

讲的已经很清楚了,直接看代码吧

#include<bits/stdc++.h>
#define lll long long
using namespace std;
lll read()
{
	lll x=0,w=1;char ch=getchar();
	while(ch>'9'||ch<'0') {if(ch=='-')w=-1;ch=getchar();}
	while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
	return x*w;
}
lll n,ave,qwe,ans;
lll a[1000010],sum[1000010];
int main()
{
	n=read();
	for(lll i=1;i<=n;i++)a[i]=read(),qwe+=a[i];
	lll ave=qwe/n;
	for(int i=1;i<=n;i++) a[i]-=ave,sum[i]=sum[i-1]+a[i];
	sort(sum+1,sum+1+n);int cut=sum[(n+1)/2];
	for(int i=1;i<=n;i++) ans+=abs(sum[i]-cut);
	cout<<ans<<endl;
}
posted @ 2018-08-07 17:19  Frozen_Heart  阅读(1165)  评论(0编辑  收藏  举报