专栏文章

P2512 [HAOI2008] 糖果传递

个人记录参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@min4f08z
此快照首次捕获于
2025/12/01 20:23
3 个月前
此快照最后确认于
2025/12/01 20:23
3 个月前
查看原文

P2512 [HAOI2008] 糖果传递

方法一:

费用流,过不了

方法二:

不难发现,传递路线不会交叉,所以如果题目不是环,是序列的情况下,可以直接 O(n)O(n) 贪心解决
考虑破环,枚举 11 号点和 nn 号点传递的量,即可变成序列的情况
显然会超时,不难发现是单峰函数,三分即可,时间复杂度为 O(nlogn)O(n\log n)

代码:

CPP
#include<bits/stdc++.h>
#define int long long 
using namespace std;
const int N=1e6+50,inf=2e9;
int n,a[N],b[N],ans=1e18,to;
int work(int x){
	int tot=abs(x);
	for(int i=1;i<=n;i++) b[i]=a[i];
	b[n]+=x; b[1]-=x;
	for(int i=1;i<n;i++){
		if(b[i]<to) tot+=to-b[i],b[i+1]-=to-b[i];
		else tot+=b[i]-to,b[i+1]+=b[i]-to;
	}
	return tot;
}
main(){
	scanf("%lld",&n);
	for(int i=1;i<=n;i++) scanf("%lld",&a[i]),to+=a[i];
	to/=n;
	int L=-inf,R=inf;
	while(L+3<=R){
		int x=L+(R-L)/3,y=L+(R-L)*2/3;
		if(work(x)<work(y)) R=y-1; 
		else L=x;
	}
	for(int i=L;i<=R;i++) ans=min(ans,work(i));
	printf("%lld\n",ans);
}

方法三:

xix_ii1i-1 号点传递给 ii 号点的个数,x1x_1nn11
那么有 ai+xixi+1=toa_i+x_i-x_{i+1}=to,即 xi+1=(aito)+xix_{i+1}=(a_i-to)+x_i
ci=aitoc_i=a_i-to,则 xi=x1+j=1i1cjx_i=x_1+\sum_{j=1}^{i-1}c_j
答案为 i=1nxi\sum_{i=1}^n |x_i|,排序后让 x1x_1 取中位数即可

代码:

CPP
#include<bits/stdc++.h>
#define int long long 
using namespace std;
const int N=1e6+50;
int n,a[N],to,c[N],ans;
main(){
	scanf("%lld",&n);
	for(int i=1;i<=n;i++) scanf("%lld",&a[i]),to+=a[i];
	to/=n;
	for(int i=2;i<=n;i++) c[i]=c[i-1]+a[i-1]-to;
	sort(c+1,c+n+1);
	int x=-c[(n+1)/2];
	for(int i=1;i<=n;i++) ans+=abs(x+c[i]);
	printf("%lld\n",ans);
}

评论

0 条评论,欢迎与作者交流。

正在加载评论...