题意:有一串数,我们用两个操作让它们的gcd不为1,1.直接删除,花费x,2.给它加一,花费y
思路:每个数都能分解成负数,我们枚举素数。关于操作的贪心:x小于等于y的话,直接删除;否则,计算x/y,可以加这么多个1,还不能满足条件的话,不如删除。
对于一个素数,有许多这样的区间[p,2p],我们考虑哪些数直接删除划算,哪些数+1到2p划算。分界点就是2p-x/y。用have[n] 记录前缀1~n中有几个数,sum[n] 记录have[n]中的这些数的和。
直接删除的数:区间起点 到 分界点前的那个点 中有几个数
加1的数:每个点都要加到2p,减去它们本来的值
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<stdlib.h>
#include<math.h>
#include<vector>
#include<list>
#include<map>
#include<stack>
#include<queue>
#include<algorithm>
#include<numeric>
#include<functional>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int maxn = 1e6+5;
ll have[maxn],sum[maxn];
int vis[maxn];
int main(void)
{
int n,x,y,s;
while(scanf("%d%d%d",&n,&x,&y)!=EOF)
{
memset(vis,0,sizeof vis);
memset(have,0,sizeof have);
memset(sum,0,sizeof sum);
for(int i = 1; i <= n; i++)
{
int a;
scanf("%d",&a);
have[a]++;sum[a]+=a;
}
for(int i = 2; i <= 1000000; i++)
{
have[i] += have[i-1];
sum[i] += sum[i-1];
}
s = x/y;
ll ans = 0x3f3f3f3f3f3f3f3f;
for(int i = 2; i <= 1000000; i++)
{
if(vis[i]) continue;
ll temp = 0;
int j;
for(j = i; j <= 1000000; j += i)
{
vis[j] = 1;
int p = max(j-i+1,j-s);
temp += (have[p-1] - have[j-i]) * x;
temp += ( (have[j] - have[p-1]) * j - (sum[j] - sum[p-1])) * y;
}
//以下3行,可以不特殊处理,只要把以上每个for循环的1000000 改成 2000000
int p = max(j-i+1,j-s);
temp += (have[min(p-1,1000000)] - have[j-i]) * x;
temp += ( (have[1000000] - have[min(p-1,1000000)]) * j - (sum[1000000] - sum[min(p-1,1000000)])) * y;
ans = min(ans,temp);
}
printf("%lld\n",ans);
}
return 0;
}