CodeForces 1305F. Kuroni and the Punishment (玄学 随机)
题意
给 \(a_1,a_2,...,a_n\),你每次操作可以对其中一个数加减 \(1\),求使 \(gcd(a_1,a_2,...,a_n)>1\) 的最小操作次数。
题解
比较玄学,题解有英文的简单解释,没大看懂 \(orz\)
首先我们称满足要求且次数最小的 \(gcd\) 为最优质因子。
这里有一个玄学的结论,随机选一个 \(a_i\),最优质因子是 \(a_i-1,a_i,a_i+1\) 其中一个数的因子的概率为 \(\frac{1}{2}\)
那么如果我们随机选了 \(50\) 个数,忽略重复情况,这些数的质因数中没有最优质因子的概率就是 \((\frac{1}{2})^{50}\),是非常小的。
所以直接随机抽 \(50\) 个数,分解质因数,计算使所有 \(a_i\) 为该质因数倍数的花费,取最小花费,就是答案了。
代码
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <map>
using namespace std;
typedef long long LL;
const int N=2e5+10;
int n;
LL a[N],ans;
map<int,int> vis;
int random(int n){
return (LL)rand()*rand()%n;
}
void check(LL x){
LL res=0;
for(int i=1;i<=n;i++)
if(a[i]>=x) res+=min(a[i]%x,x-a[i]%x);
else res+=x-a[i];
ans=min(ans,res);
}
int main(){
srand(time(0));
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
ans=n;
for(int i=1;i<=min(100,n);i++){
int id=random(n+1);
LL temp=a[random(n)+1];
for(int k=-1;k<=1;k++){
LL num=temp+k;
if(num<1) continue;
for(LL j=2;j*j<=num;j++){
if(num%j!=0) continue;
if(!vis[j]) check(j),vis[j]=1;
while(num%j==0) num/=j;
}
if(num>1) check(num);
}
}
cout<<ans<<endl;
return 0;
}