122. 糖果传递


题目链接

122. 糖果传递

\(n\) 个小朋友坐成一圈,每人有 \(a[i]\) 个糖果。

每人只能给左右两人传递糖果。

每人每次传递一个糖果代价为 \(1\)

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

输入格式

第一行输入一个正整数 \(n\),表示小朋友的个数。

接下来 \(n\) 行,每行一个整数 \(a[i]\),表示第 \(i\) 个小朋友初始得到的糖果的颗数。

输出格式

输出一个整数,表示最小代价。

数据范围

\(1≤n≤1000000,\)
\(0≤a[i]≤2×10^9,\)
数据保证一定有解。

输入样例:

4
1
2
5
4

输出样例:

4

解题思路

贪心

均分纸牌的环形情况,而存在一种最优方案,两数之间不用交换,可以当作将普通均分纸牌首尾连起来的逆过程。假设当前数 \(i\)\(i+1\) 的贡献为 \(x_i\),其中 \(x_i\) 为负数时也没有关系,因为可以认为在之前 \(i\)\(i-1\) 拿了足够的数,即最终答案为 \(\sum_{i=1}^n|x_i|\),而有如下方程:

\[\left\{\begin{matrix} x_n+a_1-x_1=avg \\ x_1+a_2-x_2=avg \\ ... \\ x_{n-1}+a_n-x_n=avg \end{matrix}\right. \]

最终答案用 \(x_1\) 表示为 \(\sum_i^n|s[i]-(i-1)\times avg+x_1|\),其中 \(s[i]\) 为将 \(a_1=0\) 时的前缀和,此最小值即转化为仓库选址问题,\(-x_1\) 为中位数时即为答案

  • 时间复杂度:\(O(n)\)

代码

// Problem: 糖果传递
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/124/
// Memory Limit: 64 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

// %%%Skyqwq
#include 
 
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
 
typedef long long LL;
typedef pair PII;
 
template  bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template  bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
 
template  void inline read(T &x) {
    int f = 1; x = 0; char s = getchar();
    while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
    while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
    x *= f;
}

const int N=1e6+5;
int n,a[N],b[N];
int main()
{
    cin>>n;
    LL sum=0,avg=0;
    for(int i=1;i<=n;i++)
    {
    	cin>>a[i];
    	sum+=a[i];
    }
    avg=sum/n;
    for(int i=2;i<=n;i++)
    	b[i]=b[i-1]+a[i]-avg;
    nth_element(b+1,b+(n+1)/2,b+1+n);
    LL res=0;
    for(int i=1;i<=n;i++)res+=abs(b[i]-b[(n+1)/2]);
    cout<