题意:
给你n个数,你有两个操作:
①将某个数删掉,消耗为x
②将某个数+1,消耗为y,这个操作可以对某个数多次使用
而你的目的是让最后n个数的Gcd不为1,求最小消耗
思路:
思路懒得打了,看下面这篇博客的吧
http://blog.csdn.net/mengxiang000000/article/details/77850732
我们知道,Gcd的结果是素数是最好的,所以我们首先筛出素数,1e6内的素数个数约为8w(8e4)个。
然后考虑对应数字是删除还是增添。很显然我们这里可以Dp去做,但是时间复杂度很爆炸,所以考虑X和Y两种操作之间的关系。
①不难想到,如果X<Y的话,我们就不用进行Y操作了。
②所以我们设定move=X/Y表示我们最多对一个数进行增添数字的次数,如果超过了这个次数,我们不如将其删除来的划算。

接下来考虑如何计算结果。首先我们肯定要O(8e4)去枚举答案,接下来考虑,既然我们已经知道了move这个信息,那么对于当前枚举的素数p,我们有一个区间【p,2p】的话,我们肯定能够通过move这个信息找到区间的一个分界点,使得【p~分界点】的数我们都将其删除,使得【分界点~2p】的数字都增添变成2p.

那么这里我们只要枚举出来一个素数p,然后每一次计算之后将区间移动p个长度即可。整体时间复杂度O(Len*LogLen)这里Len表示素数的个数。

具体实现细节都在代码的注释里

#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long ll;
int a[3000000];
long long num[3000000]= {0};
long long  ssum[3000000]= {0};
int main()
{
     int n,x,y;
    // cout<<__gcd(1000000,5000001)<<endl;

    while(cin>>n>>x>>y)
    {
        int p = x/y;
        long long ans = (long long)x*n;
        memset(num,0,sizeof(num));
        memset(ssum,0,sizeof(ssum));
        int data;
        for(int i=1; i<=n; i++)
        {
            scanf("%d",&data);
            num[data]++;
            ssum[data]+=data;
        }
        for(int i=1; i<=2000000; i++)//配合下面计算
        {
            num[i]+=num[i-1];
            ssum[i]+=ssum[i-1];
        }

        for(int i=2; i<=1000000; i++) //枚举这一群数的gcd,
            //理论上都是素数,但为了简便,枚举了所有数
        {
            long long sum = 0,nsum1,nsum2,vsum;
            for(int j=i; j<=1000000+i; j+=i)//此处要特别注意,因为
            //j+1e6 更换成2e6其实也可以 ,考虑这样一组数据,
            // 假设1e6 换成100 当i是94的时候,(1,94)(94,188)这时候
            //因为j代表的是右边界,所以区间话(94,188)超过了1e6所以跳出
            //但是这个区间内的数也是应该计算的,所以要跑到2e6
            {
                int fen = max(j-i+1,j-p);
                // 对于每个区间(j-i+1,j)找到他的分界点
                vsum = ssum[j]-ssum[fen-1];//需要增加1的数的个数
                nsum1 = num[j]-num[fen-1];//需要增加1的的数的总和
                //j*vsum为这些数需要增加到的总和
                nsum2 = num[fen-1]-num[j-i];//统计需要删除的个数
                sum+= nsum2*x;
                sum+=(j*nsum1-vsum)*y;//统计本区间内需要增加的数值
            }
           // cout<<sum<<endl;
            ans = min(ans,sum);
        }
        cout<<ans<<endl;
    }
    return 0;
}