codeforces 851D Arpa and a list of numbers

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;
}
posted @ 2018-09-28 22:17  Apocrypha  阅读(151)  评论(0编辑  收藏  举报