[BZOJ 1045] [HAOI2008] 糖果传递
题目链接:BZOJ 1045
Attention:数据范围中 n <= 10^5 ,实际数据范围比这要大,将数组开到 10^6 就没有问题了。
我们先来看一下下面的这个问题。
若 n 个人坐成一行,不围成圈,那么我们可以用 f[i] 来表示第 i 个人需要从第 i-1 个人那里获得多少个糖果。(这就是“均分纸牌”那道题)
当 f[i] < 0 时,表示第 i 个人需要给第 i-1 个人 | f[i] | 个糖果。
那么 f[i] = K - (A[i] - f[i+1]) ,其中 K 为平均分配后每个人的糖果数量。
答案 Ans 就为 Σ | f[i] | ,从 n 到 1 依次求 f[i] 即可。
再回到 BZOJ-1045 这道题。
令 f[i] 表示第 i 个人从第 i-1 个人那里获取的糖果数量。特别地,f[1] 表示第 1 个人从第 n 个人那里获取的糖果数量。
一旦我们确定其中的任何一个 f[i] ,其他的所有 f[i] 也就都随之确定了。
令 g[i] = f[i] - f[i - 1] ( 1 < i <= n ) ,那么 f[i] = S[i] + f[1] ,其中 S[i] = Σ g[j] ( 1 < j <= i ) 。特别地,S[1] = 0 。
那么最后的答案 Ans = Σ | S[i] + f[1] | = Σ | S[i] - (-f[1]) | ,即为 S[i] 到 -f[1] 的距离和。
可以发现 g[i] = f[i] - f[i-1] = f[i] - [K - (A[i-1] - f[i])] = A[i-1] - K 。
那么 g[i] , S[i] 的值都是固定的,与 f[1] 无关,问题转化为求一个数轴上的点,使数轴上 n 个定点到它的距离和最小。
我们知道,当这个动点为 n 个定点的中位数时,距离和最小。
那么就很容易写出代码。
代码如下:
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #include <algorithm> using namespace std; const int MaxN = 1000000 + 5; int n; typedef long long LL; LL Tot, K, Ans, f1; LL A[MaxN], g[MaxN], S[MaxN]; inline LL Abs(LL x) { return x > 0ll ? x : -x; } int main() { scanf("%d", &n); for (int i = 1; i <= n; i++) { scanf("%lld", &A[i]); Tot += A[i]; } K = Tot / (LL)n; for (int i = 2; i <= n; i++) g[i] = A[i - 1] - K; S[1] = 0; for (int i = 2; i <= n; i++) S[i] = S[i - 1] + g[i]; sort(S + 1, S + n + 1); f1 = -S[(n + 1) >> 1]; Ans = 0ll; for (int i = 1; i <= n; i++) Ans += Abs(S[i] + f1); printf("%lld\n", Ans); return 0; }