【bzoj1045】【HAOI2008】 糖果传递

Description

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

Input

  第一行一个正整数n<=987654321,表示小朋友的个数.接下来n行,每行一个整数ai,表示第i个小朋友得到的
糖果的颗数.

Output

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

Sample Input

4
1
2
5
4

Sample Output

4

题解:

  膜黄学长。

  首先求出平均数$p$,然后设$x_{i}$表示第i个人要给第$i-1(MOD)n$个人糖果的数量,然后得到$n$个形如$p==a_{i}-x_{i}+x_{i+1}$的方程。

  全部展开就是:

  $$p==a_{1}-x_{1}+x_{2}\rightarrow x_{2}==p-a_{1}+x_{1}$$

  $$p==a_{2}-x_{2}+x_{3}\rightarrow x_{3}==2p-a_{1}-a_{2}+x_{1}$$

  $$...$$

  $$p==a_{n}-x_{n}+x{1}\rightarrow x_{1}==x_{1}$$

  此时我们设$f_{i}=\sum_{x=1}^{i-1}a_{i}-(i-1)p$,上面的方程就可以写作:

  $$x_{2}==x_{1}-f_{2}$$

  $$x_{3}==x_{1}-f_{3}$$

  $$...$$

  $$x_{1}==x_{1}$$

  所以$ans=\sum_{i=1}^{n}|x_{i}|=\sum_{i=1}^{n}|x_{1}-f_{i}|$。

  将其转化到数轴上就是求一个点$x_{1}$,使此点距离所有的$f_{i}$之和最小。

  然后思考,将$f$排序,假设$f_{i}<=x_{1}<=f_{i+1},i\in[1,n]$(显然我们可以知道,$x_{1}$小于或大于$f$极值是不优的)。

  那么我们可以将$f$数组从i这个位置分成两部分。

  我们先假设$i<\frac{n}{2}$($i>\frac{n}{2}$可以类比)。

  $$ans=\sum_{j=1}^{i}(f_{n-j+1}-f_{x})+\sum_{j=i+1}^{n-i}f_{j}-x_{1}$$

  再化简一下就是:

  $$ans=\sum_{j=1}^{\frac{n}{2}}(f_{n-j+1}-f_{j})+\sum_{j=i+1}^{\frac{n}{2}}2(f_{i}-x_{1})$$

  然后就显然了,当我们让后面的最小时,答案最小。

  所以时,答案最小。

  记得开long long。

#include<cstdio>
#include<algorithm>
const int N=(int)1e6+10;
inline int read(){
int s=0,k=1;char ch=getchar();
  while(ch<'0'||ch>'9') k=ch=='-'?-1:k,ch=getchar();
  while(ch>47&&ch<='9') s=s*10+(ch^48),ch=getchar();
  return s*k;
}
int n;
int a[N],f[N];
inline int abs(int x){return x>0?x:-x;}
int main(){
  n=read();
  long long tot=0;
  for(int i=1;i<=n;i++){
    tot+=(a[i]=read());
  }
  tot/=n;
  f[1]=0;
  for(int i=2;i<=n;i++)
  f[i]=f[i-1]-tot+a[i];
  std::sort(f+1,f+n+1);
  int mid=f[(n>>1)+1];
  long long ans=0;
  for(int i=1;i<=n;i++)
  ans+=abs(f[i]-mid);
  printf("%lld\n",ans);
}
posted @ 2017-08-31 16:07  Troywar  阅读(232)  评论(0编辑  收藏  举报