Address
Description
- 圆桌上坐着
n
个人,每人有一定数量的金币,金币总数能被n整除。每个人可以给他左右相邻的人一些金币,最终使得每个人的金币数目相等。你的任务是求出被转手的金币数量的最小值。
- 第一行为整数
n(n≥3)
,以下
n
行每行一个正整数,按逆时针顺序给出每个人拥有的金币数。
Output
Sample Output
Interpretation
- 设四个人编号为1,2,3,4。第3个人给第2个人2个金币(变成
1,4,3,4
),第2个人和第4个人分别给第1个人1个金币。
Hint
Solution 数学
- 设
xi
表示第
i−1
个人给第
i
个人的金币数,记第i人拥有的金币数为
ai
且平均数为
ave
- 那么我们会得到
n
个形如ai+xi−1−xi=ave的方程,求最小的
∑i=1n|xi|
- 移项得到
xi=xi−1+ai−ave
这一递推式,也就是说只要知道
x1
就确定其余的
xi
- 考虑把
∑i=1nxi
表示成
∑i=1n|x1−deletei|
的形式,进一步地我们能够推出
deletei=(i−1)∗ave−∑j=2iaj
,从而直接算出
deletei
- 那么
∑i=1n|x1−deletei|
的几何意义就是找到一个最优的点
x1
使得它到
n
个点deletei的距离之和最小
- 因此
x1
毫无疑问是
deletei
序列的中位数,算出其对应的答案即可
Code
#include <iostream>
#include <cstdio>
#include <cctype>
#include <algorithm>
#include <cstring>
using namespace std;
namespace INOUT
{
const int S = 1 << 20;
char frd[S], *hed = frd + S;
const char *tal = hed;
inline char nxtChar()
{
if (hed == tal)
fread(frd, 1, S, stdin), hed = frd;
return *hed++;
}
inline int get()
{
char ch; int res = 0; bool flag = false;
while (!isdigit(ch = nxtChar()) && ch != '-');
(ch == '-' ? flag = true : res = ch ^ 48);
while (isdigit(ch = nxtChar()))
res = res * 10 + ch - 48;
return flag ? -res : res;
}
};
using namespace INOUT;
const int N = 1e5 + 5;
int sum, n, ave, del[N]; long long Ans;
int main()
{
n = get();
for (int i = 1; i <= n; ++i)
sum += get(), del[i] = -sum;
ave = sum / n; sum = 0;
for (int i = 1; i <= n; ++i)
del[i] += sum, sum += ave;
sort(del + 1, del + n + 1);
for (int i = 1, im = n >> 1; i <= im; ++i)
Ans += (long long)del[n - i + 1] - del[i];
cout << Ans << endl;
return 0;
}