洛谷 P2512 [HAOI2008]糖果传递【思维】


题意:

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


分析:

解决这题我们可以用到整体思想,先考虑不是环形的情况,因为我们要使得每个人的数量都是平均数,那么若我们将一个块看做一个整体,在其内部均分和其他数是没有关系的,至于要达到平均数,则需要与外面的数进行交换,那么这个交换的代价是多少呢
S i S_i Si代表前 i i i的糖果拥有数的前缀和, v v v是平均数,就有
a n s = ∑ i = 1 n ∣ S i − v ∣ ans=\sum_{i=1}^{n}|S_i-v| ans=i=1nSiv
再设 S i ′ S'_i Si表示 S i − v S_i-v Siv的前缀和
当考虑到环形的时候,我们所要想的就是在哪里进行断开,能使我们的答案最小
k k k代表我们枚举的断点
a n s = ∑ i = 1 n ∣ S i ′ − S k ′ ∣ ans=\sum_{i=1}^{n}|S'_i-S'_k| ans=i=1nSiSk
有个很好用的结论就是当 S k ′ S'_k Sk是中位数时,上面式子的答案就会最小

数学证明
在数轴上有 n n n个点,找出一个点 x x x,使得 t a ta ta到各个点的距离和最小
如果我们把数轴上的点两两配对,最大的配最小的,次大的配次小的 … … ……
则到每组点最近的距离的点在这两个点中间,那么如果有奇数个点,那么显然中间那个点便为所求
∴ ∴ 该点表示的数是这 n n n个数的中位数得证
f r o m from from 洛谷题解


代码:

#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
#include<cmath>
#define LL long long 
using namespace std;
inline LL read() {
    
    
    LL d=0,f=1;char s=getchar();
    while(s<'0'||s>'9'){
    
    if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){
    
    d=d*10+s-'0';s=getchar();}
    return d*f;
}
LL a[1000005],s[1000005],v=0;
int main()
{
    
    
	LL n=read();
	for(LL i=1;i<=n;i++) a[i]=read(),v+=a[i];
	v/=n;
	for(LL i=1;i<=n;i++) a[i]-=v,s[i]=s[i-1]+a[i];
	sort(s+1,s+1+n);
	LL mid=s[n/2+1];
	LL ans=0;
	for(LL i=1;i<=n;i++) ans+=abs(s[i]-mid);
	cout<<ans;
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_35786326/article/details/108781755