Kuroni and the Punishment CodeForces - 1305F 随机函数mt19937 + 质因子分解
题意:
给你n个数,你每次操作可以对一个数加1或者减1,让你求你最少需要操作多少次可以使这n个数的公因子大于1
题解:
正常方法就是枚举质因子(假设质因子为x),然后对于这个数组中的数a[i],让a[i]变成x的倍数的最小操作数为:
1、如果a[i]不为0
答案为:min(a[i]%x,x-a[i]%x)
2、a[i]为0
答案为:x
后面的思路参考博客:https://blog.csdn.net/qq_41818939/article/details/104658566
假设最大公约数为2时,每个数最多只需要操作一次就可以了,所以操作次数最多为n。所以需要操作次数≤1的数的数量≥n/2。
一个数x操作次数≤1时候会变成x,x+1,x-1三种情况,所以我们可以随机一下需要操作次数≤1的数,这时候随机不到这种数的概率
就是1/2T,T的数字大了之后概率就很小了,几乎不可能了。对每个随机到的数我们直接判断他的三种情况,每种情况就是枚举
他的质因子,然后暴力计算需要操作的次数,最后取最小值就行了。
复杂度为O(T*(sqrt(max)+n*log(max)))。
代码:
1 /* 2 (神奇的随机算法) 3 假设最大公约数为2时,每个数最多只需要操作一次就可以了,所以操作次数最多为n。所以需要操作次数≤1的数的数量≥n/2。 4 一个数x操作次数≤1时候会变成x,x+1,x-1三种情况,所以我们可以随机一下需要操作次数≤1的数,这时候随机不到这种数的概率 5 就是1/2T,T的数字大了之后概率就很小了,几乎不可能了。对每个随机到的数我们直接判断他的三种情况,每种情况就是枚举 6 他的质因子,然后暴力计算需要操作的次数,最后取最小值就行了。 7 复杂度为O(T*(sqrt(max)+n*log(max)))。 8 ———————————————— 9 原文链接:https://blog.csdn.net/qq_41818939/java/article/details/104658566 10 */ 11 #include<bits/stdc++.h> 12 using namespace std; 13 typedef long long ll; 14 mt19937 rng_32(chrono::steady_clock::now().time_since_epoch().count()); 15 ll a[200005]; 16 int n; 17 ll cal_ans(ll x) 18 { 19 ll ret=0; 20 for (int i=0;i<n;i++) 21 { 22 ll tmp=a[i]%x; 23 /*防止出现0 24 如果a[i]是0,那么tmp也是0 25 这样的话min(tmp,x-tmp)=0 26 但是这样肯定是错的,因为a[i]是0的话,是没有因子的 27 28 */ 29 if (a[i]!=tmp) 30 ret+=min(tmp,x-tmp); 31 else 32 ret+=x-tmp; 33 } 34 return ret; 35 } 36 //计算质因子 37 ll fac(ll x) 38 { 39 ll ret=1e18; 40 ll en=sqrt(x+1ll); 41 for(ll i=2;i<=en;i++) 42 { 43 if (x%i==0) 44 { 45 ret=min(ret,cal_ans(i)); 46 while(x%i==0) 47 x/=i; 48 if (x==1) 49 break; 50 } 51 } 52 if (x>1) 53 ret=min(ret,cal_ans(x)); 54 return ret; 55 } 56 int main() 57 { 58 //printf("%d\n",(0%5)); 59 cin>>n; 60 for (int i=0;i<n;i++) 61 scanf("%I64d",&a[i]); 62 int T=10; 63 ll ans=1e18; 64 while (T--) 65 { 66 ll pos=rng_32()%n; 67 //处理三种情况 68 if (a[pos]>2) 69 ans=min(ans,fac(a[pos]-1ll)); 70 ans=min(ans,fac(a[pos])); 71 ans=min(ans,fac(a[pos]+1ll)); 72 } 73 cout<<ans; 74 }