传送门:BZOJ1045
结论题。
设i顺时针传给下一个人的数量为xi,目标平均值值为ave
则目标就是让ai−xi+xi−1=ave的前提下最小化
∑i=1n|xi|
可以构造出
a1−x1+x2=ave
a2−x2+x3=ave
a1−x3+x4=ave
…..
an−xn+x1=ave
这个方程组有n个方程和n个变量,但显然,最后一个方程式无意义。
于是考虑:
a1−x1+x2=ave⟹x2=ave+x1−a1
a2−x2+x3=ave⟹x3=ave+x2−a2=2×ave−a1−a2+x1
a3−x3+x4=ave⟹x3=3×ave−a1−a2−a3+x1
…
an−xn+x1=ave,无意义
这只要自己计算一下就可以明白,记
ci=(∑ij=1aj)−i×ave
,于是有
x2=x1−c1
x3=x1−c2
……
∀i≥2,xi=x1−ci−1
于是最小化的目标函数就是
|x1|+∑i=1n−1|x1−ci|=|x1−0|+∑i=1n−1|x1−ci|
也就是说,给出数轴上的点 (0,c1,c2,c3...cn−1),求一个点y,令y到所有点的距离和最小
显然,点y就是所有点的中位数
完成推导后做法还是很优雅的。
代码上的小细节见下。
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <iostream>
#include <cmath>
#include <cstring>
using namespace std;
int n;
int a[1000005];
int c[1000005];
long long ave;
void Solve()
{
for(int i=2;i<=n;i++)
c[i]=c[i-1]+a[i]-ave;
sort(c+1,c+n+1);
long long ans=0;
int j=(1+n)/2;
for(int i=1;i<=n;i++)
ans+=abs(c[j]-c[i]);
cout<<ans;
}
void Readdata()
{
freopen("loli.in","r",stdin);
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=1;i<=n;i++)
ave+=a[i];
ave/=n;
}
void Close()
{
fclose(stdin);
fclose(stdout);
}
int main()
{
Readdata();
Solve();
Close();
return 0;\
}