[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
", Ans);
	return 0;
}

  

  

原文地址:https://www.cnblogs.com/JoeFan/p/4160899.html