codeforces 851D Arpa and a list of numbers
题意
给出(n)个数,有两种操作:
1.将一个数从数列中删除,代价为(x)。
2.将一个数加1,代价为(y)。
询问最少花费多少的代价能够使数列中所有数的(Gcd)不为1。
((1 leq n leq 5 cdot 10^5 , 1 leq x,y leq 10^9 , 1 leq a_i leq 10^5))
题解
emm...这个题目似乎无论是CF上的题解还是各种网上的题解,都是那种利用(x)和(y)之间的关系来求每个数最优的操作代价。但似乎这个题目还有一个乱搞的做法。。
首先我们肯定想到的是枚举(1~1e6)之内的所有素数,然后(Check)的贪心的进行选择(正解也是给予这个的)。但是这样的复杂度我们是接受不了的,于是我们就会不由自主地进行乱搞想更加优越的做法。首先我们会发现,无论怎样,让这所有的数的(Gcd)等于2似乎是个不错的选择,因为这样最劣的情况也只会对(n)个元素进行修改,所以我们刚开始的时候先(Check)一下2,作为当前最优的答案。然后由于复杂度的原因,我们不能枚举完所有的素数,那么怎么办?那我们就假装这个答案一定会是某个质因数,并且这个质因数在原来序列中出现过若干次,而出现的次数越多,那么作为最优答案的可能性也越大。这是为什么呢?我也不知道。。我们基于这个似乎不正确的结论,就可以贪心的进行(Check)了。先对于每一个数进行分解质因数,然后贪心的从出现次数由高到低进行枚举质因数进行(Check),大概(Check)个十多个就行了,这样我们至少就可以过了很多的数据了。至于为什么这个乱搞做法是正确的。。信息竞赛不需要证明。。只要觉得对就行了。。
啊啊啊哪个dalao来叉掉我啊,或者帮我证明一下啊。。。(逃
Code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=5e5+500;
vector<int>pri;
int n,x,y;
ll res=2e18;
int a[N];
struct Cnt {
int idx,cnt;
bool operator < (const Cnt &rhs) const {
return cnt>rhs.cnt;
}
}C[2000005];
void Get(int x) {
int c=x;
for(int i=2;i*i<=c;i++) {
if(c%i==0) C[i].cnt++;
while(c%i==0) c/=i;
}
if(c>1) C[c].cnt++;
}
void Check(int G) {
ll ans=0;
for(int i=1;i<=n;i++) {
if(a[i]%G==0) continue;
if(x<=y) {
ans+=1ll*x;
}
else {
int s=a[i]%G;
s=G-s;
ans+=min(1ll*s*y,1ll*x);
}
}
res=min(res,ans);
}
int main() {
scanf("%d%d%d",&n,&x,&y);
int Mx=0;
for(int i=1;i<=1000000;i++) C[i].idx=i,C[i].cnt=0;
for(int i=1;i<=n;i++) {
scanf("%d",&a[i]);
Get(a[i]);
}
Check(2);
sort(C+1,C+1000001);
vector<int>tmp;
tmp.clear();
for(int i=1;i<=10;i++) {
if(C[i].cnt) tmp.push_back(C[i].idx);
else break;
}
for(int i=0;i<(int)tmp.size();i++) {
Check(tmp[i]);
}
printf("%lld
",res);
return 0;
}