1. 程式人生 > 其它 >題解 模擬賽 【stone】

題解 模擬賽 【stone】

模擬賽 【stone】

題目大意:

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