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