CF1034A Enlarge GCD

题意:给你\(n\)个数,去掉尽量少的数使得剩下数的\(gcd\)比原来的大,无解输出\(-1\)

题目意思很简洁,想理出来一个清晰的思路却花了不少时间......首先先计算出总的\(GCD\),然后再把每个数都除去这个\(GCD\),接下来的事情就得仔细考虑一下了。

\(M=max\left\{a[i]\right\}\),则若枚举\(1-M\)内所有质数(显然枚举质数比合数优)并一一判断在\(1-n\)中整除它们的数字的个数,复杂度为\(O(\frac{nM}{log\,M})\)必须爆炸。当时想到这里就优化不下去了,于是凉凉。实际上有一种看似暴力的方法可以较优地解决这个问题:我们开一个数组\(b[i]\),并在每一个\(b[a[i]]\)的位置上加一,那么当我们枚举质数的时候将其所有倍数上的数组\(b\)的值加在一起就可以更新答案了。运行次数大概是\(\sum \limits_{i为质数且i \le M}\frac{M}{i}\),这东西收敛得比较快,所以复杂度可以接受

代码如下:

#include<cstdio>
#include<iostream>
#define INF 1000000000
using namespace std;
const int N=3e5+10;
const int M=1.5e7+10;
int n,a[N],GCD,primes[M],v[M],cnt,num[M],sum,ans=INF,maxn;
inline int exgcd(int x,int y){int r;while(x&&y){r=x%y;x=y;y=r;}return x;}
inline void Primes_Table(){
    for(register int i=2;i<=M-10;i++){
        if(!v[i]){v[i]=i;primes[++cnt]=i;}
        for(register int j=1;j<=cnt&&i*primes[j]<=M-10;j++){
            if(v[i]<primes[j])continue;v[i*primes[j]]=primes[j];}
    }
}
int main(){
    Primes_Table();scanf("%d",&n);
    for(register int i=1;i<=n;i++)scanf("%d",&a[i]);
    GCD=a[1];for(register int i=2;i<=n;i++)GCD=exgcd(GCD,a[i]);
    for(register int i=1;i<=n;i++)a[i]/=GCD,maxn=max(maxn,a[i]);
    for(register int i=1;i<=n;i++)num[a[i]]++;
    for(register int i=1;i<=cnt;i++){sum=0;
        for(register int j=primes[i];j<=maxn;j+=primes[i])sum+=num[j];
        ans=min(ans,n-sum);}
    printf("%d\n",ans==n? -1:ans);return 0;
}

转载于:https://www.cnblogs.com/ForwardFuture/p/9738837.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值