B. Arpa and a list of numbers
http://codeforces.com/problemset/problem/850/B
题意:
给定n个数,对每个数进行两种操作,使得剩下所有数的gcd不为1。
1、删除一个数,花费x。
2、增大一个数,每增加1,花费y。
求最小花费。
数据:
n, x ,y (1 ≤ n ≤ 5·1e5, 1 ≤ x, y ≤ 1e9)
思路:
1.枚举素数,可以按照每个数进行分块。
2.策略是 把每块内后 min( prime-1 , x/y ) 的数增加到该素数prime的倍数,之前的数全部删除。
3.用 前缀和 与 前缀个数 维护上述。
4.注意要多开一倍内存。
代码:
#include <cstdio>
#include <cstring>
#include <math.h>
#include <vector>
#include <algorithm>
#include <iostream>
#include <queue>
#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long LL;
const LL N = 2050005;
const LL M = 10921000;
const LL INF = (1LL<<60);
const LL MOD = 1e9+7;
const double eps = 1e-4;
LL n,m;
LL x,y;
LL s[N];
LL prime[N],vis[N];
LL pos=0;
void ini()
{
pos=0;
for (LL i=2;i<N;i++)
{
if (vis[i]==0)
prime[pos++] = i;
for (LL j=0;j<pos && i*prime[j]<N;j++)
{
vis[i*prime[j]] = 1;
if (i%prime[j]==0)
break;
}
}
}
LL num[N],sum[N];
int main()
{
ini();
while (~scanf("%I64d%I64d%I64d",&n,&x,&y))
{
memset(num,0,sizeof num);
memset(sum,0,sizeof sum);
LL ans = n*x;
LL tmp = 0;
LL ma = 0;
for (LL i=0;i<n;i++)
{
LL a;
scanf("%I64d",&a),ma = max(ma,a);
num[a]++;
sum[a] += a;
}
for (LL i=1;i<N;i++)
{
sum[i] += sum[i-1];
num[i] += num[i-1];
}
for (LL i=0;i<pos;i++)
{
LL e = prime[i];
tmp = 0;
for (LL j=0;j*e<N/2;j++)
{
LL ct = min(e-1,x/y);
tmp += x * (num[(j+1)*e-ct-1] - num[j*e]);
tmp += y * ((num[(j+1)*e] - num[(j+1)*e-ct-1])*(j+1)*e - (sum[(j+1)*e] - sum[(j+1)*e-ct-1]));
}
ans = min(ans,tmp);
}
printf("%I64d\n",ans);
}
return 0;
}
/*
4 23 17
1 17 17 16
10 6 2
100 49 71 73 66 96 8 60 41 63
*/