题目链接

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\n",ans);
	return 0;
}