题意
给 (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;
}