正题
luogu
CF1305F
题目大意
给你n个数,每次操作可以使一个数+1或-1,让你用最小的操作数使所有数的gcd>1
解题思路
显然把所有数都修改为偶数可以得到 2|gcd,且步数 ≤n\leq n≤n
对于其它方案,至少有一半的数修改次数小于1(如果不满足,那其他数大于一半且步数 ≥2\geq 2≥2,所以步数大于n,不如前面的方案)
所以每次随机一个数进行质因数分解,然后考虑每个质因数作为gcd的最小步数(gcd可能是合数,但是只用满足有一个质因子就好了)
这样每一次操作得到正确答案的概率最小是 12\frac{1}{2}21,如果取 k 个数得到正确答案的概率就是 2k−12k\frac{2^k-1}{2^k}2k2k−1
code
#include<map>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
#define N 200210
#define NN 1000010
using namespace std;
int n,w,p[NN];
ll x,ans,a[N],prime[NN];
map<ll,int>pp;
void work()
{for(int i=2;i<=1e6;++i){if(!p[i])prime[++w]=i;for(int j=1;j<=w&&i*prime[j]<=1e6;++j){p[i*prime[j]]=1;if(i%prime[j]==0)break;}}return;
}
ll get(ll x)
{if(pp[x])return ans;pp[x]=1;ll sum=0;for(int i=1;i<=n&&sum<ans;++i)sum+=min((a[i]>=x?a[i]%x:x),x-a[i]%x);return sum;
}
void solve(ll x)
{for(int i=1;i<=w&&prime[i]*prime[i]<=x;++i)if(x%prime[i]==0){ans=min(ans,get(prime[i]));while(x%prime[i]==0)x/=prime[i];}if(x>1)ans=min(ans,get(x));return;
}
int main()
{srand(2018729);scanf("%d",&n);work();for(int i=1;i<=n;++i){scanf("%lld",&a[i]);if(a[i]&1)ans++;}for(int k=1;k<=min(20,n/2)&&ans;++k){x=rand()*rand()%n+1;while(p[x])x=rand()*rand()%n+1;p[x]=1;solve(a[x]);solve(a[x]-1);solve(a[x]+1);}printf("%lld",ans);return 0;
}