洛谷P3156 [CQOI2011]分金币

分金币

洛谷P3156


技术统计

难度 提高+/省选-

用时 10min

提交次数 1

unaccept 次数 0

ac次数 1


题意概括

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

数据范围

3 ≤ n ≤ 100000 , 金 币 数 ≤ 1 0 9 3 \le n \le 100000,金币数\le 10^9 3n100000,109



解法一、

知识点

  1. 数学题
  2. 数学题
  3. 数学题(重要的事情说三遍)

解法详解

很显然的是,我们需要求average(平均值)。我们设一个前缀和sum[i],为前i个人转手的金币的数量之和,显然对于第i个人,他转手的金币数为sum[i+1]-sum[i]。我们可以求出来sum[1…i]-average的值。到这里,我们求过的东西仿佛与答案的关系并不大。因为我们需要求的是转手的金币的和的最小值,而转手的金币的数量是一定的,那么我们可以默认,每个人转手的方式都是最优的,那么有S=sum[1…n]-sum[i]是可能的答案。如何让S最大呢?根据数学推导(懒),sum[mid]==sum[i]时S有最大值。到这里本题的核心就结束了,求sum[mid]只需要排一下序,然后使mid=(1+n+1)>>1就好了。

坑点

  1. 因为1+n的奇偶性无从得知,所以我们可以根据c++向下取整的特性,让mid=(1+n+1)/2
  2. 一定要开long long 啊,毕竟金币数*n=10^14。

数学证明

我们设i+1给i的金币为p[i],(pi可正可负),ave为平均值,那么ans=|p[1]|+|p[2]|+…+|p[n]|,我们要求的是ans的最小值。
很显然,ave+p[i]=p[i+1]+a[i] (这里很妙,我不给出为什么,可以散发一下你们的神犇之力想想),ave-a[i]=p[i+1]-p[i],我们设w[i]=ave-a[i],s[i]=w[1]+w[2]+w[3]+…+w[i].
所以p[i]=s[i-1]-p[1]
设x=s[i-1]-p[1]
那么ans=|0-x|+|s[1]-x|+…|s[n-1]-x|;
所以若要ans的值最小,有x=s[mid](x为s[]的中位数)
综上所述:
a n s = ∑ i = 1 n ∣ s [ i ] − s [ m i d ] ∣ ans=\sum_{i=1}^{n}|s[i]-s[mid]| ans=i=1ns[i]s[mid]
PS:s即为sum。

代码实现

#include<bits/stdc++.h>
#define ll long long
#define maxn 1000100
using namespace std;
inline ll read()
{
    ll f=1,k=0;
    char c=getchar();
    while(c>'9'||c<'0')
    {
        if(c=='-')f=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9')k=(k<<1)+(k<<3)+(c^48),c=getchar();
    return f*k;
}
inline void write(ll x)
{
    if(x<0)putchar('-'),x=-x;
    if(x>9)write(x/10);
    putchar(x%10+'0');
}
ll n,m,aver;
ll a[maxn];
int main()
{
    n=read();
    for(int i=1;i<=n;i++)
    {
        a[i]=read();
        aver+=a[i];
    }
    aver/=n;
    for(int i=1;i<=n;i++)
    {
        a[i]-=aver;//节约空间,不想再开一个sum数组了
        a[i]+=a[i-1];
    }
    sort(a+1,a+n+1);
    aver=0;
    ll mid=(2+n)>>1;
    for(int i=1;i<=n;i++) aver+=abs(a[i]-a[mid]);
    write(aver);
    system("puse");
    return 0;
}


解法二、

听dalao们说可以用费用流求,然而数据范围不允许dalao们散发神犇的光辉



类似题目

  1. 分糖果(双倍经验)
  2. 均分纸牌
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值