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

troysps 2020-03-04

题目链接:

https://codeforces.com/contest/1305/problem/F

题意:

 给出$n$个数,最少执行多少次操作可以使得$n$个数最大公约数不为1

每次操作可以给某个数加一或者减一

分析:

由于因子为$2$时,最多需要执行$n$次操作

所以在寻找到最优因子的情况下,执行两次操作的数的数量不可能多于$\frac{1}{2}$

那么任意选择数组中的一个数$a[i]$,最优因子是$a[i],a[i]+1,a[i]-1$的因子的概率是$\frac{1}{2}$

那么在数组随机选出$100$个数,最优因子不是他们因子的概率是$\left (\frac{1}{2} \right )^{100}$

注意:rand()返回的是1到32767的随机数,想要产生1到2e5的随机数需要两个rand相乘

 AC代码:

#include <bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for (int i=a;i<=b;i++)
#define per(i,a,b) for (int i=b;i>=a;i--)
#define pb push_back
#define fi first
#define se second
#define SZ(x) ((int)(x).size())

typedef long long ll;
typedef vector<int> VI;
typedef pair<int,int> PII;

const int maxn=2e5+7;
const int INF=1e9+10;

set<ll>se;
int n;
ll a[maxn];
void add(ll x){
    for(ll i=2;i*i<=x;i++){
        if(x%i==0){
            se.insert(i);
            while(x%i==0)x/=i;
        }
    }
    if(x!=1)se.insert(x);
}
ll cal(ll x){
    ll res=0;
    rep(i,1,n){
        if(a[i]>=x)res+=min(a[i]%x,x-a[i]%x);
        else res+=x-a[i]%x;
        if(res>=n)return n;
    }
//    cout<<x<<" "<<res<<endl;
    return res;
}
int main()
{
    srand((unsigned)time(NULL));
    scanf("%d",&n);
    rep(i,1,n)scanf("%lld",&a[i]);
    rep(i,1,10*n){//随机打乱数组的顺序
        int x=(ll)rand()*rand()%n+1;
        int y=(ll)rand()*rand()%n+1;
        swap(a[x],a[y]);
    }
    rep(i,1,min(100,n)){
        add(a[i]);
        add(a[i]+1);
        if(a[i]-1>=2)add(a[i]-1);
    }
    ll ans=n;
    se.insert(2);
    for(auto i:se){
//        cout<<i<<endl;
        ans=min(ans,cal(i));
    }

    printf("%lld\n",ans);
    return 0;
}

相关推荐