Codeforces 1305 F. Kuroni and the Punishment(随机算法)

题目链接
题目大意:
给定一个数组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;
}
发布了12 篇原创文章 · 获赞 1 · 访问量 327

猜你喜欢

转载自blog.csdn.net/qq_41818939/article/details/104658566