第13届景驰-埃森哲杯广东工业大学ACM程序设计大赛

地址:https://ac.nowcoder.com/acm/contest/90/C

思路:和同学讨论了半天结果是道数学题,打扰了。。。

博客:https://blog.csdn.net/nka_kun/article/details/79714666

先看怎么处理一个圈有n个人只给相邻的人硬币的解法,保证总数整除n是必然的,
有个很明显的情况就是如果A给了B,那么B将不会再给A,这样无疑是多余的步骤,
所以相邻的两个人AB之间要么是A给B,要么是B给A,那么相邻的第i-1,i,i+1三个人,
不妨假设第i个人给了第i-1个人x[i]个硬币,从第i+1个人中拿到了x[i+1]个硬币,
其中x[i-1]可以是负数,代表是i-1给i硬币,
假设最终每个人有M个硬币,初始状态第i个人有ai个硬币,那么有以下等式:
对于第一个人,a1-x1+x2=M ==> x2=x1-(a1-M)=x1-C1 (C1=a1-M,下面类似)
对于第二个人,a2-x2+x3=M ==> x3=x2-(a2-M)=x2-C2
对于第三个人,a3-x3+x4=M ==> x4=x3-(a3-M)=x3-C3 
...
...
...
将x2,x3,xi用x1代入得
x1=x1
x2=x1-C1
x3=x1-(C1+C2)
x4=x1-(C1+C2+C3)
xi=x1-(C1+C2+C3+...+C[i-1]) 
若di=前i个Ci的和 sum{c1,c2,...,ci}
则 xi=x1-di

所有式子带入之后其实就是求
ans=min{|x1|+|x2|+|x3|+....+|xi|+...+|xn|}
即 ans=min {|x1|+|x1-d1|+|x1-d2|+...+|xn-d[n-1]|}
这个其实就是在一个坐标轴上找一点x使得x和0,d1,d2,...,dn的距离之和最短,这个是求中位数,
对这些数排序一下x取中位数就好了

现在看有n个人隔k个人才能给硬币,其实这个可以看做gcd(n,k+1)个圈,
然后他们之间相邻的两个人可以交换硬币,第1个人依次和1+(k+1),1+2(k+1)...形成一个圈,
剩余的人也是这样,这样就是处理gcd(n,k+1)个圈就是了,最终结果全部相加就是答案,
注意一些特殊情况,k=n-1或者k=n其实是不能交换的状态,这个特判一下就好了

然后我已经能过了,结果因为一个bug,即前缀和d[i]的d[0]由于排序了因此要初始化,然后一直不知道哪里错了,找了一晚上的BUG。。。

(╯°Д°)╯︵┻━┻   

┬—┬ ノ( ' - 'ノ) 摆好摆好   

 (╯°O°)╯( ┻━┻再掀一次

(╯°Д°)╯( ┻━┻再特么的掀一次

Code:

#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;

const int MAX_N=1e6+5;
int n,m;
LL a[MAX_N];
LL d[MAX_N];

int main()
{
	ios::sync_with_stdio(false);
	cin>>n>>m;
	LL Sum=0;
	for(int i=0;i<n;Sum+=a[i],++i)
		cin>>a[i];
	LL res=0;
	if(Sum%n){
		res=-1;
	}else if(m>=n-1){
		for(int i=1;i<n;++i)
			if(a[i]!=a[i-1]){
				res=-1;	break;
			}
	}else{
		Sum/=n;
		int k,s;
		for(int i=0;i<n;++i)
			if(a[i]!=-1){
				k=i;	s=1;
				d[0]=0;	//排序了要初始化啊啊啊啊啊!!!! 
				while(a[k]!=-1){
					d[s]=d[s-1]+a[k]-Sum;	a[k]=-1;
					k=(k+m+1)%n;	++s;
				}
				if(d[s-1]){
					res=-1;	break;
				}
				sort(d,d+s-1);
				LL x=d[(s-1)/2];
				for(int i=0;i<s-1;++i)
					res+=abs(x-d[i]);
			}
	}
	if(res!=-1)	cout<<res<<endl;
	else	cout<<"gg"<<endl;
	
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值