Codeforces 1305F Kuroni and the Punishment (随机化)

题目链接

https://codeforces.com/contest/1305/problem/F

题解

真的想不出……然而大家都会
考虑枚举最终所有数的 (gcd),设为 (g). (g=2)时操作次数不超过 (n), 故答案不超过 (n).
对于 (g>2), 只有操作次数小于 (n) 时才有贡献。那么考虑每个元素的操作次数,肯定存在至少一个元素的操作次数为 (0), 因此只需要枚举那些至少是输入中一个数的因数的数;又不难发现只需要枚举质数。以上都是铺垫,现在离正解只差一个Key observation.
观察到绝大部分情况下操作次数都是 (0)(1). 具体来讲,至少有一半的数操作次数都不超过 (1). 于是每次随机选序列中的一个数 (x),检验 (x-1,x,x+1) 的所有质因子即可,设进行 (T) 轮,则正确率至少为 ((1-frac{1}{2^T})).
时间复杂度 (O(T(sqrt W+n))),其中 (W=max a_i).

代码

#include<bits/stdc++.h>
#define llong long long
#define mkpr make_pair
#define riterator reverse_iterator
using namespace std;

inline int read()
{
	int x = 0,f = 1; char ch = getchar();
	for(;!isdigit(ch);ch=getchar()) {if(ch=='-') f = -1;}
	for(; isdigit(ch);ch=getchar()) {x = x*10+ch-48;}
	return x*f;
}

const int N = 2e5;
const int MX = 1e6;
bool isp[MX+3]; int pri[MX+3];
llong a[N+3];
map<llong,int> done;
int n,prin; llong ans;

int randw() {return (rand()<<15)|rand();}

void EulerSieve()
{
	isp[1] = 1;
	for(int i=2; i<=MX; i++)
	{
		if(!isp[i]) {pri[++prin] = i;}
		for(int j=1; j<=prin&&i*pri[j]<=MX; j++)
		{
			isp[i*pri[j]] = 1;
			if(i%pri[j]==0) break;
		}
	}
}

void check(llong x)
{
	if(done.count(x)) return; done[x] = 1;
	llong ret = 0ll;
	for(int i=1; i<=n; i++)
	{
		ret += a[i]<x?x-a[i]%x:min(a[i]%x,x-a[i]%x);
	}
	ans = min(ans,ret);
}
void check1(llong x)
{
	for(llong i=2; i*i<=x; i++)
	{
		if(isp[i]) continue;
		if(x%i==0)
		{
			check(i);
			while(x%i==0) {x/=i;}
		}
	}
	if(x>1) {check(x);}
}

int main()
{
	srand(time(NULL));
	EulerSieve();
	scanf("%d",&n);
	for(int i=1; i<=n; i++) scanf("%lld",&a[i]);
	for(int i=1; i<=n; i++) {ans += a[i]&1;}
	for(int T=1; T<=20; T++)
	{
		int i = randw()%n+1;
		check1(a[i]); check1(a[i]-1); check1(a[i]+1);
	}
	printf("%I64d
",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/suncongbo/p/12555523.html