troysps 2020-03-04
https://codeforces.com/contest/1305/problem/F
给出$n$个数,最少执行多少次操作可以使得$n$个数最大公约数不为1
每次操作可以给某个数加一或者减一
由于因子为$2$时,最多需要执行$n$次操作
所以在寻找到最优因子的情况下,执行两次操作的数的数量不可能多于$\frac{1}{2}$
那么任意选择数组中的一个数$a[i]$,最优因子是$a[i],a[i]+1,a[i]-1$的因子的概率是$\frac{1}{2}$
那么在数组随机选出$100$个数,最优因子不是他们因子的概率是$\left (\frac{1}{2} \right )^{100}$
注意:rand()返回的是1到32767的随机数,想要产生1到2e5的随机数需要两个rand相乘
#include <bits/stdc++.h> using namespace std; #define rep(i,a,b) for (int i=a;i<=b;i++) #define per(i,a,b) for (int i=b;i>=a;i--) #define pb push_back #define fi first #define se second #define SZ(x) ((int)(x).size()) typedef long long ll; typedef vector<int> VI; typedef pair<int,int> PII; const int maxn=2e5+7; const int INF=1e9+10; set<ll>se; int n; ll a[maxn]; void add(ll x){ for(ll i=2;i*i<=x;i++){ if(x%i==0){ se.insert(i); while(x%i==0)x/=i; } } if(x!=1)se.insert(x); } ll cal(ll x){ ll res=0; rep(i,1,n){ if(a[i]>=x)res+=min(a[i]%x,x-a[i]%x); else res+=x-a[i]%x; if(res>=n)return n; } // cout<<x<<" "<<res<<endl; return res; } int main() { srand((unsigned)time(NULL)); scanf("%d",&n); rep(i,1,n)scanf("%lld",&a[i]); rep(i,1,10*n){//随机打乱数组的顺序 int x=(ll)rand()*rand()%n+1; int y=(ll)rand()*rand()%n+1; swap(a[x],a[y]); } rep(i,1,min(100,n)){ add(a[i]); add(a[i]+1); if(a[i]-1>=2)add(a[i]-1); } ll ans=n; se.insert(2); for(auto i:se){ // cout<<i<<endl; ans=min(ans,cal(i)); } printf("%lld\n",ans); return 0; }