题目链接
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;
}