CodeForces - 851D Arpa and a list of numbers (数学+思维)

本文提出一种高效算法,通过枚举素数并利用前缀和优化计算过程,实现给定数列经最少代价操作后最大公约数不为1的目标。适用于数值操作与算法优化场景。

题意:

给定n个数字,要求操作后n个数字的gcd不为1,并且操作的代价最小,每个数字有两种操作,删除的代价为x,数字加1的代价为y。

分析:

最暴力的想法当然是枚举gcd(听说可以过)

但是所有数的gcd当然是一个素数的倍数,那么我们枚举素数就可以了。

若当前数字是 a,设 k = a%p 那么可以知道

如果k \in [0,\frac{x}{y})那么加上1

如果k \in [\frac{x}{y},p)那么删除该数

so:

删除的代价为:该区间在数组中的数字个数*x

加上1的代价为:(该区间在数组中的数字个数*p-该区间在数组中的数字和)*y

但是取余计算个数跟和每次计算都得枚举一遍,复杂度有点大,所以采用枚举素数长度的区间。

那么 区间个数 和  区间和  就可以采用前缀和计算。

so

如果a \in (p-\frac{x}{y},p]那么加上1,

如果a \in (0,p-\frac{x}{y}]那么删除该数

#include<bits/stdc++.h>

using namespace std;

typedef long long ll;

const int maxn=1e6+10;

bool flag[maxn];
int prime[maxn],pi;

void get_prime(){
    pi=0;
    memset(flag,0,sizeof flag);
    for(int i=2;i<maxn;i++){
        if(!flag[i]) prime[pi++]=i;
        for(int j=0;j<pi&&i*prime[j]<maxn;j++){
            flag[i*prime[j]]=true;
            if(i%prime[j]==0) break;
        }
    }
}

ll num[maxn*3],sum[maxn*3],a[maxn*3];

int main(){

    get_prime();
    ll n,x,y;
    scanf("%lld%lld%lld",&n,&x,&y);
    for(int i=0;i<n;i++){
        ll tmp;
        scanf("%lld",&tmp);
        a[tmp]++;
    }
    for(int i=1;i<maxn*3;i++){
        num[i]=num[i-1]+a[i];
        sum[i]=sum[i-1]+1ll*i*a[i];
    }
    ll ans=LONG_LONG_MAX;
    ll mov=(x+y-1)/y;
    for(int i=0;i<pi;i++){
        ll p=prime[i];
        ll tmp=0;
        for(ll j=0;j<=(ll)maxn+p;j+=p){
            tmp+=(num[j+max(p-mov,0ll)]-num[j])*x;
            tmp+=((num[j+p]-num[j+max(p-mov,0ll)])*(j+p)-(sum[j+p]-sum[j+max(p-mov,0ll)]))*y;
        }
        ans=min(ans,tmp);
    }
    printf("%lld\n",ans);
    return 0;
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值