Codeforces 1305F. Kuroni and the Punishment 题解

题目链接:F. Kuroni and the Punishment

题目大意:给定一个正整数序列\(a_1,a_2,\cdots,a_n\),你每一次操作可以使每一个数\(+1\)\(-1\),但必须保证操作后的数仍为正整数,问最少用多少次操作才能使它们的最大公约数不为\(1\)


题解:这样的题拿到之后第一反应就是瞎搞(然而博主太菜了赛时瞎搞了16发都没对),肯定是枚举素因子然后计算答案了,我们先假设我们已经知道了素因子是什么,假设其为\(p\),所以有一个很明显的贪心策略,就是每一个数只会变成与它相邻的两个是\(p\)的倍数的正整数,所以我们就可以得到一个\(O(n)\)的贪心策略。先把\(2\)\(3\)跑一遍(因为这两个最容易成为答案),然后就是随机了,随机抽取序列中的一个数,令其为\(x\),然后把\(x\)分解质因数,对它的每一个质因数都跑一遍,然后再这么操作\(x-1\)\(x+1\),这样的话算法错误的概率是\(\frac{1}{2}\),然后重新随机。这样随机 20 次之后,程序错误的概率就是\(2^{-20}\),当然了,赛场上没有必要这么算,直接卡时间就可以了。

下面是代码(随机函数最好用 mt19937 ,可能是博主太菜了用 rand 结果WA7):

#include <ctime>
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
template<typename Elem>
void read(Elem &a){
    a=0;
    char c=getchar();
    while(c<'0'||c>'9'){
        c=getchar();
    }
    while(c>='0'&&c<='9'){
        a=(a<<1)+(a<<3)+(c^48);
        c=getchar();
    }
}
const int Maxn=200000;
typedef long long ll;
int n;
ll a[Maxn+5];
ll p[Maxn+5];
int p_len;
void div(ll x){
    p_len=0;
    for(ll i=2;i*i<=x;i++){
        if(x%i==0){
            p[++p_len]=i;
            while(x%i==0){
                x/=i;
            }
        }
    }
    if(x>1){
        p[++p_len]=x;
    }
}
ll work(ll p){
    ll ans=0;
    for(int i=1;i<=n;i++){
        if(a[i]<p){
            ans+=p-a[i];
            if(ans>=n){
                break;
            }
            continue;
        }
        ll fron=a[i]/p;
        ll bac=(fron+1)*p;
        fron*=p;
        ans+=min(a[i]-fron,bac-a[i]);
        if(ans>=n){
            break;
        }
    }
    return ans;
}
int main(){
    clock_t begin,end;
    begin=clock();
    mt19937 rnd(time(NULL));
    read(n);
    for(int i=1;i<=n;i++){
        read(a[i]);
    }
    ll ans=n;
    ll num=0;
    for(int i=1;i<=n;i++){
        if(a[i]&1){
            num++;
        }
    }
    ans=min(ans,num);
    num=0;
    for(int i=1;i<=n;i++){
        if(a[i]==1){
            num+=2;
        }
        else if(a[i]%3!=0){
            num++;
        }
    }
    ans=min(ans,num);
    while(1){
        int x=rnd()%n+1;
        div(a[x]);
        for(int i=1;i<=p_len;i++){
            ans=min(ans,work(p[i]));
        }
        div(a[x]-1);
        for(int i=1;i<=p_len;i++){
            ans=min(ans,work(p[i]));
        }
        div(a[x]+1);
        for(int i=1;i<=p_len;i++){
            ans=min(ans,work(p[i]));
        }
        end=clock();
        if((end-begin)*1.0/CLOCKS_PER_SEC>2.0){
            break;
        }
    }
    cout<<ans<<endl;
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/withhope/p/12407721.html