BZOJ3293 [CQOI2011]分金币

58 篇文章 0 订阅

Address

Description

  • 圆桌上坐着 n 个人,每人有一定数量的金币,金币总数能被n整除。每个人可以给他左右相邻的人一些金币,最终使得每个人的金币数目相等。你的任务是求出被转手的金币数量的最小值。

Input

  • 第一行为整数 n(n3) ,以下 n 行每行一个正整数,按逆时针顺序给出每个人拥有的金币数。

Output

  • 输出被转手金币数量的最小值。

Sample Input

  • 4
    1
    2
    5
    4

Sample Output

  • 4

Interpretation

  • 设四个人编号为1,2,3,4。第3个人给第2个人2个金币(变成 1,4,3,4 ),第2个人和第4个人分别给第1个人1个金币。

Hint

  • N100000 ,总金币数 109

Solution 数学

  • xi 表示第 i1 个人给第 i 个人的金币数,记第i人拥有的金币数为 ai 且平均数为 ave
  • 那么我们会得到 n 个形如ai+xi1xi=ave的方程,求最小的 i=1n|xi|
  • 移项得到 xi=xi1+aiave 这一递推式,也就是说只要知道 x1 就确定其余的 xi
  • 考虑把 i=1nxi 表示成 i=1n|x1deletei| 的形式,进一步地我们能够推出 deletei=(i1)avej=2iaj ,从而直接算出 deletei
  • 那么 i=1n|x1deletei| 的几何意义就是找到一个最优的点 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; 
    //del[i]都减去a[1]显然对结果没有影响 
    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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值