1 条题解
-
0
本题需要使用 Pollard-Rho 算法。
算法讲解
我们需要了解:
同余的性质:如果两个整数 满足 (即 和 除以 的余数相同),那么它们的差 一定是 的倍数。
则 的结果一定是 的倍数。
∵ 是 的因数,
∴ 。
∵ ,
∴ 。
令 $d = \gcd(|x - y|, N) = \gcd(m \cdot p, k \cdot p) = p \cdot \gcd(m, k)$。
当 时,那么 就等于 。
当 时, 是 的一个非平凡因数。
假设 的最小素因数是 。我们在模 的意义下随机选取数字,模 的余数只有 共 种可能。
根据生日悖论的数学推导:从 个元素中随机抽取,大约只需要抽取 次,就有超过 50% 的概率出现两个相同的元素(即 )。
因为 ,所以 。 这意味着,我们平均只需要生成 个数字,就能找到一对满足条件的 。
我们不能真的用系统随机数,因为我们需要序列具有确定性和可重复性,以便进行数学推导。
我们定义一个简单的二次多项式函数:
其中 是一个常数(通常 )。
我们从起点 开始,不断迭代:
$$x_2 = f(x_1), \quad x_3 = f(x_2), \quad \dots, \quad x_{k+1} = f(x_k)$$由于模 的余数只有 种,这个序列 迟早会出现重复,从而进入循环。

如图,由于序列排列形成的形状酷似希腊字母 ,所以该算法得名 rho 。
如果 ,那么 。 $f(x) - f(y) = (x^2+c) - (y^2+c) = x^2 - y^2 = (x-y)(x+y)$ 。因为 ,所以 。
这意味着:如果在取模 下还没进环,但在取模 下可能已经进环了。因此我们应该找取模 下的环。我们可以使用 Floyd 判环法 或改进的 Brent 判环法 。
Floyd 判环法
设两个变量 (慢指针)和 (快指针):
初始使 。让 每次走 1 步,即 ,让 每次走 2 步,即 。当 时意味着我们成功找到环了。
这个算法 scy 曾经讲过,我就不讲太多了Brent 判环法
floyd 方法每走一步都要算一次 ,而 本身也有开销。Brent 方法让快指针按 的步长跳跃,减少了迭代次数,比 Floyd 快约 24%。
倍增优化:
不是每一步都算 ,而是先累积多步的差值乘积 ,每隔 步(如 )再算一次 。这样把多次 合并成一次,进一步降低了常数复杂度。
代码
#include<bits/stdc++.h> #define int __int128 #define abs(x) ((x)>=0?(x):-(x)) using namespace std; const int prime[]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47}; template<typename T>inline void qr(T &x){ int f=1;x=0; char c=getchar(); for(;!isdigit(c);c=getchar())f=-1; for(;isdigit(c);c=getchar())x=x*10+c-'0'; x*=f; } template<typename T>inline void qw(T x){ if(x<0) putchar('-'),x=-x; if(x/10)qw(x/10); putchar(x%10+'0'); } inline int qpow(int a,int b,int m){ int res=1; a%=m; for(;b;b>>=1,a=(a*a)%m) if(b&1) res=(res*a)%m; return res; } inline bool Is_prime(int x){ // Miller-Rabin素性测试 if(x<2)return false; if(x<=37){ for(int p:prime)if(x==p)return true; return false; } int d=x-1,s=0; while(!(d&1))d>>=1,s++; for(int y:prime){ if(y>=x)break; int t=qpow(y,d,x); if(t==1||t==x-1)continue; bool flg=false; for(int j=1;j<=s;j++){ t=t*t%x; if(t==x-1){ flg=true; break; } } if(!flg)return false; } return true; } inline int f(int x,int c,int m){return (x*x%m+c)%m;} // 伪随机迭代函数 inline int rho(int n){ // Pollard-Rho找因数 if(n==4)return 2; int c=rand(); int x=f(rand(),c,n); // 慢指针 int y=f(x,c,n); // 快指针 for(int m=1;;m=min(m<<1,(int)128)){ // 倍增批量GCD优化 if(x==y)break; // 模n下相遇,探测失败 int cnt=1; for(int i=0;i<m;i++){ cnt=(cnt*abs(x-y))%n; // 累积差值乘积 if(!cnt)break; x=f(x,c,n); // 走1步 y=f(f(y,c,n),c,n);// 走2步 } int d=__gcd(cnt,n); // 批量求GCD if(d!=1)return d; // 找到因数 } return n; // 返回n表示失败 } int ans; inline void dfs(int n){ // 递归分解 int d=rho(n); while(d==n)d=rho(n); // 失败则重试 int d2=n/d; if(Is_prime(d))ans=max(ans,d); else dfs(d); if(Is_prime(d2))ans=max(ans,d2); else dfs(d2); } inline int ga(int x){ // 获取最大素因数 if(Is_prime(x))return x; ans=0; dfs(x); return ans; } signed main(){ srand(time(0)); int q; qr(q); while(q--){ int x; qr(x); vector<int>v; while(x!=1){ // 不断剥离最大素因数 int p=ga(x); v.push_back(p); x/=p; } sort(v.begin(),v.end()); qw(v.size()); putchar(' '); for(int t:v){ qw(t); putchar(' '); } puts(""); } }
- 1
信息
- ID
- 3253
- 时间
- 1000ms
- 内存
- 1024MiB
- 难度
- 9
- 标签
- 递交数
- 63
- 已通过
- 4
- 上传者