# codeforce 1350 F. Kuroni and the Punishment (思维+随机化大法+shuffle洗牌)

题意

给出n<=2e5个数,对每一个数x<=1e12进行一次操作可以使其+1,-1,求最小的操作次数,使得最后整个序列的gcd不为1,并且所有数都是正数

思路

整个序列的gcd不为1,也就是变换后序列每个数都有一个公共因子p,一个数x经过变换之后含因子p需要操作的次数为:

  1. if(x<p) cnt=p-x%p
  2. if(x>=p) cnt=min(p-x%p,x%p)

如果当gcd为2时,每个数最多只需要操作一次,那么操作次数上限就是n。
上限是n可以得出如果有某些数操作次数大于1,那么这些数的个数一定小于(lfloor frac{n}{2} floor),如果大于,总次数就会大于n。

随机在序列中选一个数,它只需要操作<=1次的概率为(frac{1}{2}),随机选30个数,里面没有一个数是只操作<=1次的概率是(1-frac{1}{2}^{30}),概率值极小。所以可以随机取30个数,将每个数x,x-1,x+1的质因子求出来,最终序列的公共因子p就存在这些质因子中。

随机取序列中的30个数,可以使用洗牌函数shuffle,以当前时间作为随机数种子。
细节参考shuffle详解

代码

#include <bits/stdc++.h>
using namespace std;
const int maxn=2e5+5;
typedef long long LL;
LL a[maxn];
set<LL> s;
int n;

inline void addPrime(LL x){
    int sqr=sqrt(x);
    for(int i=2;i<=sqr;++i)
        if(x%i==0){
            s.insert(i);
            while (x%i==0)x/=i;
        }
    if(x>1)s.insert(x);
}
inline LL sol(LL p){
    LL res=0;
    for(int i=0;i<n;++i){
        LL y=a[i];
        if(a[i]<p)res+=(p-y%p);
        else res+=min(p-y%p,y%p);
    }
    return res;
}
int main(){
    cin>>n;
    for(int i=0;i<n;i++)cin>>a[i];

    unsigned seed=chrono::system_clock::now().time_since_epoch().count();
    shuffle(a,a+n,default_random_engine(seed));

    for(int i=0;i<n&&i<30;++i){
        addPrime(a[i]);
        addPrime(a[i]+1);
        if(a[i]>1)addPrime(a[i]-1);
    }

    LL mx=maxn;
    for(LL x : s){
        mx=min(mx,sol(x));
    }
    cout<<mx<<endl;
    return 0;
}
原文地址:https://www.cnblogs.com/sstealer/p/13297709.html