题目
给你n个正整数,你每次可以选择一个数加一或减一。 所有位置始终要为正整数。 请求出使得所有数的gcd不为1的最小操作次数。 N<=2e5 Ai<=1e12
思路
如果你假定gcd=d,只需要扫一遍就可以求出答案。 注意到,d=2时答案上界是n。 所以无论答案是什么,至少有一半的数至多被操作一次。 这引导我们搞一个不确定性的算法。 随机取一个ai,然后将ai,ai+1,ai-1的所有质因数试一次。求出答案的概率是50%。 多求几次即可
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std
;
const int N
=2e5+10;
int n
;
ll a
[N
];
set
<ll
> checked
,calced
;
ll
check(ll x
){
if(checked
.count(x
))return LLONG_MAX
;
checked
.insert(x
);
ll ans
=0;
for(int i
=1;i
<=n
;i
++)if(a
[i
]<=x
){
ans
+=x
-a
[i
];
}else{
ans
+= min(a
[i
]%x
,x
-a
[i
]%x
);
}
return ans
;
}
ll
calc(ll x
){
if(!x
||calced
.count(x
))return LLONG_MAX
;
calced
.insert(x
);
ll ans
=LLONG_MAX
;
for(ll i
=2;i
*i
<=x
;i
++)if(x
%i
==0){
ans
= min(ans
,check(i
));
while(x
%i
==0)x
/=i
;
}
if(x
!=1)ans
= min(ans
,check(x
));
return ans
;
}
int main(){
scanf("%d",&n
);
srand(time(NULL));
for(int i
=1;i
<=n
;i
++) scanf("%lld",&a
[i
]);
for(int i
=n
;i
>=1;i
--) swap(a
[i
],a
[rand()%i
+1]);
ll ans
=LLONG_MAX
;
for(int k
=1;k
<=n
&&clock()/(double)CLOCKS_PER_SEC
<.1;k
++){
ans
= min(ans
,calc(a
[k
]));
ans
= min(ans
,calc(a
[k
]+1));
ans
= min(ans
,calc(a
[k
]-1));
}
printf("%lld\n",ans
);
}