[HAOI2008] 糖果传递

非常经典的数学题。

\(x_i\) 表示 \(i\) 给右边的人多少糖(如果 \(x_i<0\),就是从右边的人那里拿糖)。先考虑列出方程

\[\left\{\begin{matrix} a_1-x_1+x_n=\bar a \\ a_2-x_2+x_1=\bar a \\ \cdots \\ a_n-x_n+x_{n-1} =\bar a \\ \end{matrix}\right. \]

\(x_1\) 表示 \(x_2\)\(x_2=a_2+x_1-\bar a\)

\(x_2\) 表示 \(x_3\)\(x_3=a_3+x_2-\bar a\)

因此,用 \(x_1\) 表示 \(x_3\)\(x_3=a_3+a_2+x_1-\bar a-\bar a\)

类似的,可知

\[x_i=\sum_{j=2}^i (a_j-\bar a)+x_1\\ \]

换成减法,

\[x_i=x_1-\sum_{j=2}^i (\bar a-a_j) \]

题目要求最小化 \(\sum |x_i|\)。设 \(s_i=\sum_{j=2}^i (\bar a-a_j)\),即最小化

\[\sum_{i=1}^n |x_1-s_i| \]

这是一个经典的问题,当 \(x_1\)\(s\) 的中位数时原式最小。

// Title:  糖果传递
// Source: HAOI2008
// Author: Jerrywang
#include <bits/stdc++.h>
#define F first
#define S second
#define pii pair<int, int>
#define ll long long
#define rep(i, s, t) for(int i=s; i<=t; ++i)
#define debug(x) cerr<<#x<<":"<<x<<endl;
const int N=1000010;
using namespace std;

int n, a[N]; ll avg, res, s[N];

int main()
{
    #ifdef Jerrywang
    freopen("E:/OI/in.txt", "r", stdin);
    #endif
    scanf("%d", &n);
    rep(i, 1, n) scanf("%d", a+i), avg+=a[i];
    avg/=n;
    rep(i, 2, n) s[i]=s[i-1]+avg-a[i];
    sort(s+1, s+n+1);
    int j=n/2+1; ll res=0;
    rep(i, 1, n) res+=abs(s[j]-s[i]);
    printf("%lld", res);
    
    return 0;
}
posted @ 2024-04-20 08:51  JosephusWang  阅读(24)  评论(0)    收藏  举报