数学题 糖果传递 bzoj

推公式

对于每一个小孩,得到或者是拿出只可能是来自相邻小孩

所以对于每一个小孩的贡献度我们可以算出来

xi: 第i个小孩给 xi 个糖果给 i-1位小孩

ave : 平均值

推倒过程如下:

ans = |x1|+...+|xn|;

a1 - x1 + x2 = ave;

令 c1 = a1 - ave;

=> x2 = x1 - c1;

同理

a2  - x2 + x3 = ave;

=> x3 = x2 - (a2-ave) = x1 - c1 - (a2 - ave) = x1 - (c1+(a2 - ave));

令 c2 = a2 - ave + c1;

同理 x3 .... xn

现在就转化为求

|x1| + |x1-c1| + |x1 - c2|+....+ |x1-cn-1| 的最小值

每个c可以求得

那么x1 其实就是中位数,这个问题就求解了

代码如下:

/*  ^^ ====== ^^ 
ID: meixiuxiu
PROG: test
LANG: C++11
*/
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <climits>
#include <string>
#include <vector>
#include <cmath>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <sstream>
#include <cctype>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int ,int> pii;
#define MEM(a,b) memset(a,b,sizeof a)
#define CLR(a) memset(a,0,sizeof a);
#define pi acos(-1.0)
#define maxn 40000
#define maxv 100005
const int inf = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
//#define LOCAL
int a[1000000];
ll b[1000000];
int main()
{
#ifdef LOCAL
	freopen("in.txt", "r", stdin);
//	freopen("out.txt","w",stdout);
#endif
	int n;cin >> n;
	int i;
	for(i=1;i<=n;i++)scanf("%d",&a[i]);
	ll sum = 0;
	for(i=1;i<=n;i++){
		sum += a[i];
	}
	ll ave = sum/n;
	b[1] = 0;
	for(i=2;i<=n;i++){
		b[i] = b[i-1] + a[i-1]-ave;
	}
	sort(b+1,b+1+n);
	ll mid;
	if(n&2){
		mid = b[n/2+1];
	}
	else mid = (b[n/2]+b[n/2+1])/2;
	ll tot = 0;
	for(i=1;i<=n;i++){
		tot += fabs(mid-b[i]);
	}
	printf("%lld\n",tot);
	return 0;
}



你可能感兴趣的:(数学)