题意:对于一个数组a[i],删去一个数字的代价为x,给一个数字+1的代价为y,求将整个数组的gcd变为不为1的最小代价。
首先我们可以枚举这个数组的gcd,由于最差情况是删一个数字为5*10^5,+1代价为1,所以gcd最大可能为10^6,接着如果暴力判断每一个数字是n方的复杂度,接着我们可以对每一个数字进行分组。
由于我们只能进行加法,所以我们可以将每[(j-1)*gcd+1,j*gcd]分为一组,他们如果要加肯定变成j*gcd最优,又因为我们最多加的次数为x/y,如果超过了这个x/y,删掉这个数的代价则会更小,所以我们就可以再将这个分组再分成两组[(j-1)*gcd+1,j*gcd-x/y-1],[j*gcd-x/y,j*gcd],对于第一组的数,我们将它直接删掉,第二组内的数字将它加到j*gcd,那么对于第一组数字,我们通过前缀和即可求出数字数量,答案*x即为这一组的花费,对于第二组的数字,我们可以也用前缀和的方法,求出这个区间内数字之和sum,以及数字个数cnt,那么他们要变成的数字之和为cnt*j*gcd,他们当前的和为sum,那么这组花费就是y*(cnt*j*gcd-sum)。
最后枚举gcd求出最小花费的一种情况即可,由调和级数可以得到复杂度为nlogn.
下附AC代码。
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define maxn 2000005
using namespace std;
typedef long long ll;
int n,maxx;
ll x,y;
ll cnt[maxn],sum[maxn];
int main()
{
scanf("%d%I64d%I64d",&n,&x,&y);
for(int i=1;i<=n;i++)
{
int t;
scanf("%d",&t);
maxx=max(t,maxx);
cnt[t]++;
sum[t]+=t;
}
for(int i=1;i<=2*1000000;i++)
cnt[i]+=cnt[i-1],sum[i]+=sum[i-1];
ll ans=x*n;
for(int i=2;i<=1000000;i++)
{
ll now=0;
for(int j=i;j<=2*1000000;j+=i)
{
ll l=max((ll)j-i+1,j-(x/y));
now+=y*(j*(cnt[j]-cnt[l-1])-(sum[j]-sum[l-1]));
now+=x*(cnt[l-1]-cnt[j-i]);
}
ans=min(ans,now);
}
printf("%I64d\n",ans);
}