目录
- codeforces 851D Arpa and a list of numbers
- 题意
- 题解
- Code
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\n",res);
return 0;
}
原文地址:https://www.cnblogs.com/Apocrypha/p/9721270.html