题意:
给出一个序列,两种操作:
1.删除一个数,代价为x
2.给一个数+1,代价为y
求最小代价,使这个序列不为空,且所有的数的gcd>1
n<=5e5,a[i]<=1e6
其实思路还是很简单的。
可以发现a[i]只有1e6,那么我们直接暴力枚举修改后的数列的gcd(为下文方便,我们假设挡当前枚举的数为i)。
那么对于一个不是i的倍数的数,它就是要么删,要么就是加到最接近它的i的倍数。
所以接下来就是暴力枚举i的倍数。
如果我们当前的倍数为j,那么易发现大小[ji-1,ji-x/y]的数,就是累加到ji,大小[(j-1)i+1,ji-x/y-1]的数就是删掉。
那这个代价怎么算?
用个桶+前缀和搞下就行了。
#include<cstdio>
#include<ctime>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<string>
#define rg register
#define il inline
#define vd void
#define ll long long
#define maxn 2000010
#define N 500010
#define For(i,x,y) for (rg int i=(x);i<=(y);i++)
#define Dow(i,x,y) for (rg int i=(x);i>=(y);i--)
#define cross(i,k) for (rg int i=first[k];i;i=last[i])
using namespace std;
il ll max(ll x,ll y){return x>y?x:y;}
il ll min(ll x,ll y){return x<y?x:y;}
il ll read(){
ll x=0;int ch=getchar(),f=1;
while (!isdigit(ch)&&(ch!='-')&&(ch!=EOF)) ch=getchar();
if (ch=='-'){f=-1;ch=getchar();}
while (isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}
int n,Max,a[N];
ll k,l,r,x,y,z,ans,Sum,cnt[maxn],sum[maxn];
int main(){
n=read(),x=read(),y=read(),z=x/y;
For(i,1,n) a[i]=read(),Max=max(Max,a[i]),cnt[a[i]]++;
For(i,1,Max*2) sum[i]=sum[i-1]+cnt[i]*i,cnt[i]+=cnt[i-1];
ans=1000000000000000ll;
For(i,2,max(Max,2)){
Sum=0;
if (Max%i==0) k=Max/i;
else k=Max/i+1;
if (i<=z)
For(j,1,k){
l=(j-1)*i,r=j*i;
Sum+=(r*(cnt[r-1]-cnt[l])-sum[r-1]+sum[l])*y;
}
else
For(j,1,k){
l=(j-1)*i,r=j*i;
Sum+=(cnt[r-z-1]-cnt[l])*x+(r*(cnt[r-1]-cnt[r-z-1])-sum[r-1]+sum[r-z-1])*y;
}
ans=min(ans,Sum);
}
printf("%lld",ans);
}