Codeforces 1305 F Kuroni and the Punishment —— 概率,随机化

This way

题意:

给你n个数,你可以对每个数进行任意次操作,每次操作要么+1,要么-1,并且要使得每个数是正整数,问你最少进行多少次操作可以使得最终所有数的gcd>1

题解:

好像有几次做到过随机思想的题目了,然而这次还是没想到那方面,感觉有必要将这个当做一个正规的知识点了,因为确实是好想法而且不容易想到。
由于2的存在,使得答案最大不会超过n,也就是将n个操作分到n个数上,那么至少有n/2个数的操作次数<=1。那么如果随机一个点i,计算a[i]-1,a[i],a[i]+1这三个数的所有质因子是答案的概率为1/2,那么当随机次数很多的时候,从概率学上来说,就是一定成功了。
那么为什么是质因子,因为如果一个数作为衡量的话,第i个数需要的操作次数是a[i]%x,x-a[i]%x中较小的那一个,那么如果x有因子,对于第i个数来说,明显更优,因为x的倍数一定是它的倍数,然后对于操作次数来说又比x要小。
然后对于1e12来说,一个数所含质因子的个数就十几个,那么我就随机化了100次来做。
还有我才注意到一点:srand((unsigned)time(NULL))的生成数范围很小,只有大概1e5不到的样子,所以需要将其转换成0-1中的某一个值,然后再*n+1,这才是正确的范围。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=1e6+5;
ll p[N],np[N],cnt;
void init(){
    for(ll i=2;i<N;i++){
        if(!np[i]){
            p[++cnt]=i;
            for(ll j=i*i;j<N;j+=i)
                np[j]=1;
        }
    }
}
ll a[N];
set<ll>s;
void deal(ll x){
    if(x<2)return ;
    for(ll i=1;p[i]<=sqrt(x)+1;i++){
        if(x%p[i]==0){
            s.insert(p[i]);
            while(x%p[i]==0)
                x/=p[i];
        }
    }
    if(x>1)
        s.insert(x);
}
int main()
{
    srand((int)time(NULL));
    init();
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%lld",&a[i]);
    for(int i=1;i<=100;i++){
        int p=(int)(rand()/double(RAND_MAX)*n)+1;
        deal(a[p]),deal(a[p]+1),deal(a[p]-1);
    }
    ll ans=1e9;
    for(auto v:s){
        ll sum=0;
        for(int i=1;i<=n;i++)
            sum+=min(a[i]>=v?a[i]%v:v,v-a[i]%v);
        ans=min(ans,sum);
    }
    printf("%lld\n",ans);
    return 0;
}

发布了584 篇原创文章 · 获赞 33 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/tianyizhicheng/article/details/104806552