题目链接
题目大意:
给定一个数组A,每次操作可以对数组中的某一个数进行+1或-1操作(操作完要保证是正整数)。要使最后的数组满足这个数组的所有数的最大公约数不为1。问最小的操作次数。
解题思路:
(神奇的随机算法)
假设最大公约数为2时,每个数最多只需要操作一次就可以了,所以操作次数最多为n。所以需要操作次数≤1的数的数量≥n/2。一个数x操作次数≤1时候会变成x,x+1,x-1三种情况,所以我们可以随机一下需要操作次数≤1的数,这时候随机不到这种数的概率就是1/2T,T的数字大了之后概率就很小了,几乎不可能了。对每个随机到的数我们直接判断他的三种情况,每种情况就是枚举他的质因子,然后暴力计算需要操作的次数,最后取最小值就行了。复杂度为
O(T*(sqrt(max)+n*log(max)))。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
mt19937 rng_32(chrono::steady_clock::now().time_since_epoch().count());
ll a[200005];
int n;
ll cal_ans(ll x)
{
ll ret=0;
for (int i=0;i<n;i++)
{
ll tmp=a[i]%x;
//防止出现0
if (a[i]!=tmp)
ret+=min(tmp,x-tmp);
else
ret+=x-tmp;
}
return ret;
}
//计算质因子
ll fac(ll x)
{
ll ret=1e18;
ll en=sqrt(x+1ll);
for(ll i=2;i<=en;i++)
{
if (x%i==0)
{
ret=min(ret,cal_ans(i));
while(x%i==0)
x/=i;
if (x==1)
break;
}
}
if (x>1)
ret=min(ret,cal_ans(x));
return ret;
}
int main()
{
cin>>n;
for (int i=0;i<n;i++)
scanf("%I64d",&a[i]);
int T=10;
ll ans=1e18;
while (T--)
{
ll pos=rng_32()%n;
//处理三种情况
if (a[pos]>2)
ans=min(ans,fac(a[pos]-1ll));
ans=min(ans,fac(a[pos]));
ans=min(ans,fac(a[pos]+1ll));
}
cout<<ans;
}