BZOJ 1045: [HAOI2008] 糖果传递 数学,乱搞

Description

  有n个小朋友坐成一圈,每人有ai个糖果。每人只能给左右两人传递糖果。每人每次传递一个糖果代价为1。
Input

  第一行一个正整数n<=987654321,表示小朋友的个数.接下来n行,每行一个整数ai,表示第i个小朋友得到的
糖果的颗数.
Output

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

1

2

5

4
Sample Output
4

解题方法: 设i顺时针传给下一个人的数量为xi,目标平均值值为ave。则目标就是让ai−xi+x(i-1)−1=ave的前提下最小化。
BZOJ 1045: [HAOI2008] 糖果传递 数学,乱搞_第1张图片

可以构造出
a1−x1+x2=ave
a2−x2+x3=ave
a1−x3+x4=ave
…..
an−xn+x1=ave

这个方程组有n个方程和n个变量,但显然,最后一个方程式无意义。
于是考虑:
a1−x1+x2=ave⟹x2=ave+x1−a1
a2−x2+x3=ave⟹x3=ave+x2−a2=2×ave−a1−a2+x1
a3−x3+x4=ave⟹x4=3×ave−a1−a2−a3+x1

记c[i]=(sigma(a[j]),j从到i)-i*ave
所以:

x2=x1-c1
x3=x1-c2
….

我们希望Xi的绝对值之和尽量小,即|x1| + |x1-c1| + |x1-c2| + ……+ |x1-c(n-1)|要尽量小。注意到|x1-ci|的几何意义是数轴上的点x1到xi的距离,所以问题变成了:给定数轴上的n个点,找出一个到他们的距离之和尽量小的点,而这个点就是这些数中的中位数,证明略。上面的c是直接替换之后的结果,也是下面代码里面的c,画一下就明白了。(0,c1,c2,c3”’,c[n-1])的中位数。

#include 
using namespace std;
const int N = 1000010;
int a[N], c[N];
int n;
int main(){
    scanf("%d", &n);
    long long ave = 0;
    for(int i = 1; i <= n; i++){
        scanf("%d", &a[i]);
        ave += a[i];
    }
    ave /= n;
    for(int i = 2; i <= n; i++){
        c[i] = c[i-1] + a[i] - ave;
    }
    sort(c + 1, c + n + 1);
    long long ans = 0;
    int j = (n + 1) / 2;
    for(int i = 1; i <= n; i++){
        ans += abs(c[i] - c[j]);
    }
    printf("%lld\n", ans);
    return 0;
}

你可能感兴趣的:(ACM/ICPC_,BZOJ)