题解 模拟赛 【stone】

模拟赛 【stone】

题目大意:

image

solution:

显然最后每个数都是 \(x\) 的倍数,那么它们的和必是 \(x\) 的倍数, \(x\) 必是和的因数且是质因数。

简单证明\(^1\)

设最后每个数为 \(k_1x\)\(k_2x\)\(k_3x\)...\(k_{n-1}x\)\(k_nx\)。它们的和\(sum=k_1x+k_2x+k_3x+...+k_{n-1}x+k_nx\),根据分配律 \(sum=(k_1+k_2+k_3+...+k_{n-1}+k_n)x\)。所以和是 \(x\) 的倍数。

证毕。

简单证明\(^2\)

反证法:
\(x\) 不是和的质因数,根据唯一分解定理 \(x\) 必有一个质因子是 \(sum\) 的因数。用更小的质因数更优,贪心的想,移动次数更少。所以 \(x\)\(sum\) 的质因数。

证毕。

然后我们就可以枚举 \(sum\) 的质因数,再枚举每堆石子,看比成为 \(x\) 倍还多几个,存起来。然后从大到小排序,因为越多,离 \(x\) 的倍数差的越少,能移动次数最少。求个组数,然后把差累加进总数再和答案取个 \(\min\) 就行了。

细节处理:

  • \(\text{long long}\)
  • 答案初始值赋大点;
  • 先线性筛出质数会比根号分解快点。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e5+5;
typedef long long LL;
LL a[N],b[N],sum,ans;
LL pr[N],cnt;
bool check[N];
inline void init() {//线性筛
	for(int i=2; i<N; i++) {
		if(!check[i]) pr[++cnt]=i;
		for(int j=1; j<=cnt; j++) {
			if(i*pr[j]>N) break;
			check[i*pr[j]]=1;
			if(i%pr[j]==0) break;
		}
	}
}
LL yue[N],num;
inline void dec(LL x) {//质因数分解
	for(int i=1; i<=cnt; i++) {
		if(x%pr[i]==0) yue[++num]=pr[i];
		while(x%pr[i]==0) x/=pr[i];
	}
	if(x>1) yue[++num]=x;
}
int main() {
	init();
	sum=0,ans=9223372036854775807;
	int n;
	scanf("%d",&n);
	for(int i=1; i<=n; i++) {
		scanf("%lld",&a[i]);
		sum+=a[i];
	}
	dec(sum);
	for(int i=1; i<=num; i++) {//枚举质因数
		LL res=0,he=0;
		LL x=yue[i];
		for(int j=1; j<=n; j++) {
			b[j]=a[j]%x;//看比 x 的倍数多几个
			he+=b[j];//存起来
		}
		sort(b+1,b+n+1);//从大到小排序
		LL ge=he/x;//求组数
		for(int j=n; j>=n-ge+1; j--)//倒着整
			res+=x-b[j];//把差的加起来
		ans=min(ans,res);//取min
	}
	printf("%lld\n",ans);//long long
	return 0;
}

End

posted @ 2021-08-09 11:31  Mr_think  阅读(49)  评论(0)    收藏  举报