題面
分析
1.暴力做法
首先先把每個數除以
可以的時間內分解出所有數的質因數,然後統計出現次數最多的質因數,設最多出現次數爲,然後把其他的數去掉就可以了,答案爲
例:
處理後
我們可以看出質因數2出現了2次,3出現了1次,5出現了2次
出現次數最多的質因數爲2或5,均出現了2次
故答案爲4-2=2
2.優化
可以看出算法的瓶頸在質因數分解,我們考慮如何優化質因數分解算法
這是一般的質因數分解算法
set<int>S;
for(int i=2; i<=x; i++) {
while(x%i==0 &&x != i) {
n/=i;
S.push(i);
}
if(x == i) {
S.push(i)
break;
}
}
該算法的時間複雜度爲
問題在於該算法需要枚舉能整除x的數,效率比較低,如果對於每個數x,我們知道能被整除x的最小質數minprime[x],算法的效率就可以提高了
那麼如何求出minprime呢?
我們想到線性篩法的過程
for(int i=2; i<=n; i++) {
if(!vis[i]) {
prime[++k]=i;
}
for(ll j=1; j<=k&&(ll)i*prime[j]<=n; j++) {
minprime[(ll)i*prime[j]]=prime[j];
vis[(ll)i*prime[j]]=1;
if(!(i%prime[j])) break;
}
}
其中的每個合數只會被篩一次,而prime[j]不正好是能整除的最小質數嗎?
因此可以把線性篩改成這個樣子
for(int i=2; i<=n; i++) {
if(!vis[i]) {
minprime[i]=i;
prime[++k]=i;
}
for(ll j=1; j<=k&&(ll)i*prime[j]<=n; j++) {
minprime[(ll)i*prime[j]]=prime[j];
vis[(ll)i*prime[j]]=1;
if(!(i%prime[j])) break;
}
}
我們就求出了minprime,注意若p是質數,minprime[p]=p
然後可以寫出分解質因數的算法
if(!vis[x]||x==1) {//如果是1或質數,直接返回
cnt[x]++;
return;
}
while(x>1) {
int t=minprime[x];
cnt[t]++;
while(x%t==0&&x!=1) {
x/=t;
}
}
因爲每次循環x至少要除以2,所以時間複雜度爲
代碼
#include<iostream>
#include<cstdio>
#include<cstring>
#define maxn 300005
#define maxv 15000005
using namespace std;
typedef long long ll;
inline int gcd(int a,int b) {
return b==0?a:gcd(b,a%b);
}
int n;
int a[maxn];
int vis[maxv];
int minprime[maxv];
int cnt[maxv];
int prime[maxv];
int k=0;
void sieve(int n) {
for(int i=2; i<=n; i++) {
if(!vis[i]) {
minprime[i]=i;
prime[++k]=i;
}
for(ll j=1; j<=k&&(ll)i*prime[j]<=n; j++) {
minprime[(ll)i*prime[j]]=prime[j];
vis[(ll)i*prime[j]]=1;
if(!(i%prime[j])) break;
}
}
}
void div(int x) {
if(!vis[x]||x==1) {
cnt[x]++;
return;
}
while(x>1) {
int t=minprime[x];
cnt[t]++;
while(x%t==0&&x!=1) {
x/=t;
}
}
}
int main() {
scanf("%d",&n);
sieve(15000000);
int g=0;
for(int i=1; i<=n; i++) {
scanf("%d",&a[i]);
g=gcd(g,a[i]);
}
for(int i=1; i<=n; i++) {
a[i]/=g;
}
for(int i=1; i<=n; i++) {
div(a[i]);
}
int ans=0;
for(int i=2; i<=15000000; i++) {
ians=max(ans,cnt[i]);
}
if(ans==0) printf("-1\n");
else printf("%d\n",n-ans);
}