CodeForces 1305F. Kuroni and the Punishment (玄学 随机)

传送门

题意

\(a_1,a_2,...,a_n\),你每次操作可以对其中一个数加减 \(1\),求使 \(gcd(a_1,a_2,...,a_n)>1\) 的最小操作次数。

题解

比较玄学,题解有英文的简单解释,没大看懂 \(orz\)
首先我们称满足要求且次数最小的 \(gcd\) 为最优质因子。
这里有一个玄学的结论,随机选一个 \(a_i\),最优质因子是 \(a_i-1,a_i,a_i+1\) 其中一个数的因子的概率为 \(\frac{1}{2}\)
那么如果我们随机选了 \(50\) 个数,忽略重复情况,这些数的质因数中没有最优质因子的概率就是 \((\frac{1}{2})^{50}\),是非常小的。
所以直接随机抽 \(50\) 个数,分解质因数,计算使所有 \(a_i\) 为该质因数倍数的花费,取最小花费,就是答案了。

代码

#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <map>
using namespace std;
typedef long long LL;
const int N=2e5+10;
int n;
LL a[N],ans;
map<int,int> vis;

int random(int n){
	return (LL)rand()*rand()%n;
}

void check(LL x){
	LL res=0;
	for(int i=1;i<=n;i++)
		if(a[i]>=x) res+=min(a[i]%x,x-a[i]%x);
		else res+=x-a[i];
	ans=min(ans,res);
}

int main(){
	srand(time(0));
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
	ans=n;
	for(int i=1;i<=min(100,n);i++){
		int id=random(n+1);
		LL temp=a[random(n)+1];
		for(int k=-1;k<=1;k++){
			LL num=temp+k;
			if(num<1) continue;
			for(LL j=2;j*j<=num;j++){
				if(num%j!=0) continue;
				if(!vis[j]) check(j),vis[j]=1;
				while(num%j==0) num/=j;
			}
			if(num>1) check(num);
		}
	}
	cout<<ans<<endl;
	return 0;
}
posted @ 2020-03-04 15:11  BakaCirno  阅读(268)  评论(1编辑  收藏  举报