zoukankan      html  css  js  c++  java
  • # 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;
    }
    
  • 相关阅读:
    Mutex和RWMutex
    内核态和用户态
    runtime源码
    goroutine的结束与通信
    进程线程协程
    堆和栈
    array和slice区别
    逃逸分析
    单例模式
    WaitGroup用法
  • 原文地址:https://www.cnblogs.com/sstealer/p/13297709.html
Copyright © 2011-2022 走看看