題解 模擬賽 【stone】
阿新 • • 發佈:2021-08-09
模擬賽 【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\) 倍還多幾個,存起來。然後從大到小排序,因為越多,離 \(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; }