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;
}
原文地址:https://www.cnblogs.com/BakaCirno/p/12409651.html