P2512 [HAOI2008]糖果传递——解题报告

一、题目链接

P2512 [HAOI2008]糖果传递

二、题目大意

n n n个小朋友坐成一圈,每人有 a i a_i ai个糖果。每人只能给左右两人传递糖果。每人每次传递一个糖果代价为 1 1 1。求使所有人获得均等糖果的最小代价。

数据范围: 1 ≤ n ≤ 1 0 6 1\leq n \leq10^6 1n106

三、题目分析

  1. 假设 T [ i ] T[i] T[i]表示第 i i i个人向第 i − 1 i−1 i1个人传了 T [ i ] T[i] T[i]张牌 ( T [ 1 ] T[1] T[1]表示第一个向第 n n n个),值为负表示反向。代价为 ∑ 1 × ∣ T [ i ] ∣ \sum 1\times|T[i]| 1×T[i]

  2. 由于传递后每个人的牌都为 a v e r a g e average average ,所以可以列出一系列方程
    a [ 1 ] + T [ 2 ] − T [ 1 ] = a v e r a g e a [ 2 ] + T [ 3 ] − T [ 2 ] = a v e r a g e … … a [ n ] + T [ 1 ] − T [ n ] = a v e r a g e (1) \begin{gather*} a[1]+T[2]−T[1]=average\\ a[2]+T[3]−T[2]=average\\ ……\\ a[n]+T[1]−T[n]=average \end{gather*}\tag{1} a[1]+T[2]T[1]=averagea[2]+T[3]T[2]=average……a[n]+T[1]T[n]=average(1)

  3. T [ 1 ] T[1] T[1]表示 T [ 2 ] , T [ 3 ] , … , T [ n ] T[2],T[3],…,T[n] T[2],T[3],,T[n]可得:
    T [ 2 ] = T [ 1 ] − ( a [ 1 ] − a v e r a g e ) T [ 3 ] = T [ 1 ] − ( a [ 2 ] + a [ 1 ] − 2 × a v e r a g e ) … … T [ n ] = T [ 1 ] − ( a [ n − 1 ] + a [ n − 2 ] + ⋯ + a [ 1 ] − ( n − 1 ) × a v e r a g e ) (2) \begin{gather*} T[2]=T[1]−(a[1]−average)\\ T[3]=T[1]−(a[2]+a[1]−2 \times average)\\ ……\\ T[n]=T[1]−(a[n−1]+a[n−2]+⋯+a[1]−(n−1) \times average)\\ \end{gather*}\tag{2} T[2]=T[1](a[1]average)T[3]=T[1](a[2]+a[1]2×average)……T[n]=T[1](a[n1]+a[n2]++a[1](n1)×average)(2)

  4. s i s_i si为前 i i i个人糖果数与平均值的差值的前缀和
    T [ 1 ] = T [ 1 ] − s [ n ] T [ 2 ] = T [ 1 ] − s [ 1 ] T [ 3 ] = T [ 1 ] − s [ 2 ] . . . . . . T [ n ] = T [ 1 ] − s [ n − 1 ] (3) \begin{gather*} T[1]=T[1]-s[n]\\ T[2]=T[1]−s[1]\\ T[3]=T[1]−s[2]\\ ......\\ T[n]=T[1]−s[n-1]\\ \end{gather*}\tag{3} T[1]=T[1]s[n]T[2]=T[1]s[1]T[3]=T[1]s[2]......T[n]=T[1]s[n1](3)
    操作次数可以表示为 ∑ ∣ T [ i ] ∣ = ∑ ∣ T [ 1 ] − s [ i ] ∣ \sum|T[i]|=\sum|T[1]−s[i]| T[i]=T[1]s[i],那么原问题变成了求 T [ 1 ] T[1] T[1]的值使得上述求和最小。

  5. 此时,对 T [ 1 ] T[1] T[1]的选择即可转化为数轴上几点 s [ i ] s[i] s[i]到动点 T [ 1 ] T[1] T[1]距离和的最小值。很显然:

    • n n n为奇数,当 T [ 1 ] T[1] T[1] s [ i ] s[i] s[i]中位数时和最小。
    • n n n为偶数,当 T [ 1 ] T[1] T[1] s [ i ] s[i] s[i]中间两数之间任何值时和最小。

四、正解程序

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
typedef long long ll;
ll a[1000010],b[1000010],mid,ans,average,n;
int main()
{
    scanf("%lld",&n);
    for(ll i=1;i<=n;i++)
    {
        scanf("%lld",&a[i]);
        average+=a[i];
    }
    average/=n;
    for(ll i=1;i<=n;i++)
        b[i]=b[i-1]+average-a[i];
    sort(b+1,b+1+n);
    ll temp=b[n/2+1],ans=0;
    for(ll i=1;i<=n;i++)
        ans+=abs(b[i]-temp);
    printf("%lld",ans);
    
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值