题意:
给定n个数字,要求操作后n个数字的gcd不为1,并且操作的代价最小,每个数字有两种操作,删除的代价为x,数字加1的代价为y。
分析:
最暴力的想法当然是枚举gcd(听说可以过)
但是所有数的gcd当然是一个素数的倍数,那么我们枚举素数就可以了。
若当前数字是 a,设 k = a%p 那么可以知道
如果那么加上1
如果那么删除该数
so:
删除的代价为:该区间在数组中的数字个数*x
加上1的代价为:(该区间在数组中的数字个数*p-该区间在数组中的数字和)*y
但是取余计算个数跟和每次计算都得枚举一遍,复杂度有点大,所以采用枚举素数长度的区间。
那么 区间个数 和 区间和 就可以采用前缀和计算。
so
如果那么加上1,
如果那么删除该数
#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;
}