嘘~ 正在从服务器偷取页面 . . .

洛谷P2512 [HAOI2008]糖果传递 题解


洛谷P2512 [HAOI2008]糖果传递 题解

题目链接:P2512 [HAOI2008]糖果传递

题意

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

输入格式

小朋友个数 $n$,下面 $n$ 行 $a_i$。

输出格式

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

数据范围

对于 $100\%$ 的数据 $n\le 10^6$。

设最后每个人的糖果为 $\bar{a} = \frac{1}{n} \sum_{1 \le i \le n} a_i$ ,记 $b_i = a_i -\bar{a}$

则题目变为如何将 $b_i$ 全部转化为 $0$ 。

设 $x_i$ 表示最终方案中 $i$ 给 $i+1$ 了多少糖果(如果 $x_i$ 为负,则代表 $i+1$ 给了 $i$ 多少)

由 $b_i=0$ 得

则答案为 $\sum_{i=1}^{n} |x_i|$ ,考虑最小化它。

注意到这里一共有 $n$ 个方程,而独立的仅有 $n-1$ 个

也就是说,任意 $n-1$ 个方程都可以推出剩下那个

于是设 $t=x_1$ ,则

记 $S_i = \sum_{k=2}^{i} a_k$ ,问题转化为最小化

记 $x=-t$ ,则问题转化为

在数轴上找一已知点 $x$ 使其与 $n$ 个已知点距离之和最小,显然取中位数位置的那个点。

时间复杂度 $O(n)$ ,用nth_element()来找中位数。

代码:

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cstdarg>
#include <cmath>
#include <iomanip>
#include <random>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
#define N ((int)(1e6+15))

int n,s,p,a[N];
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    cin >> n;
    for(int i=1; i<=n; i++) cin >> a[i], s+=a[i]; s/=n;
    for(int i=1; i<=n; i++) a[i] = a[i] + a[i-1] - s;
    nth_element(a+1,a+(p = (n+1)>>1),a+1+n); s=0;
    for(int i=1; i<=n; i++) s+=abs(a[i]-a[p]);
    cout << s << '\n';
    return 0;
}

参考文献

[1] https://yhx-12243.github.io/OI-transit/records/lydsy1045%3Blg1368%3Blg2512.html


文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录