笔者
笔者曾获得 ICPC 2020 世界总决赛资格,ICPC 2020 亚洲区域总决赛第五名。
本次内容
本次内容主要围绕质因数分解和欧几里得算法两部分展开,主要内容如下:
质因数分解
- 暴力
- Pollard Rho
欧几里得算法
- GCD
- 拓展 GCD
- 类 GCD
质因数分解
对于一个质数
在
中出现的次数为
暴力
暴力分解一个数
的时间复杂度是
。
代码语言:javascript复制#define LL long long
int fac[100000];
void get_fac(LL n)
{
int num=0;
for (LL i=2;i*i<=n;i )
{
while(n%i==0) fac[num ]=i,n/=i;
}
if (n>1) fac[num ]=n;
}
Pollard Rho
分解一个合数的高效算法。
可以将问题转化为求一个合数
的非平凡因子(非平凡因子是指除了
和本身以外的最小约数)。
从生日悖论可以得到,我们在
内随机
次整数,就有大概率出现两个相同的数。
于是我们可以考虑随机一个
出来,然后有一个多项式
,我们令
。
也就是通过大概
次,我们可以得到
,因为
固定,所以之后就会有
,也就是说
数列是
形的,也就是会入一个环,之后一直在环内循环往复。
因为得到这样的
,我们就可以得到
。
考虑如何找到这样的循环节,显然把所有的
都保存下来不太现实,因为
可能会比较大。
有一个方式是用两个指针,一个指针每次走一步,另一个指针每次走两步,这样当后一个指针入环以后,显然最多再进行环长步数就一定会重合(从奇偶性考虑),也就是找到循环节重合的位置时间复杂度为
。
而我们要找的是非平凡因子,显然合数非平凡因子一定
,我们令
,那么我们只需要在
意义下找这样的
,这样找到一个非平凡因子的期望时间复杂度是
。
代码语言:javascript复制mt19937 mt(time(0));
LL f(LL v,LL n,LL c){LL t=mul(v,v,n) c;return t<n?t:t%n;};
LL pollard_rho(LL n,LL c)
{
LL x = uniform_int_distribution<LL>(1,n-1)(mt),y=x;
while (1){
x=f(x,n,c);y=f(f(y,n,c),n,c);
if (x==y) return n;
LL d=gcd(abs(x-y),n);
if (d!=1) return d;
}
}
LL fac[100], fcnt;
void get_fac(LL n,LL cc=19260817)
{
if (n==4){fac[fcnt ]=2;fac[fcnt ]=2;return;}
if (primeQ(n)){fac[fcnt ]=n;return;}
LL p=n;
while (p==n) p=pollard_rho(n,--cc);
get_fac(p);get_fac(n/p);
}
void go_fac(LL n){fcnt=0;if (n>1) get_fac(n);}
欧几里得算法
GCD
于是有
代码语言:javascript复制void GCD(LL x,LL y)
{
LL z=x%y;
while(z) x=y,y=z,z=x%y;
return y;
}
LCM
需要注意的是,为了防止溢出,需要先做除法再做乘法
代码语言:javascript复制void LCM(LL x,LL y)
{
return x/GCD(x,y)*y;
}
裴蜀定理
对于整数
,如果
有整数解,那么一定有
拓展 GCD
用于求解
的任意整数解。
考虑在求 GCD 的过程中同时求出
,我们令
(操作一步以后)
如果递归下去,则相当于现在已经求得
的
了
,则
于是可以得到新的
代码语言:javascript复制LL ex_gcd(LL a,LL b,LL &x,LL &y)
{
if (b==0) {x=1;y=0;return a;}
LL ret=ex_gcd(b,a%b,y,x);
y-=a/b*x;
return ret;
}
这样可以求得一个任意解
,而一般题目会对解的范围有一定的要求,可以发现,经过下面调整,仍然是一个题目的解:
。
类 GCD
类欧几里得最基本的就是求
,其中
,为了方便,我们用
来表示这个式子。
可以先理解一下这个式子的含义,如果把
看作坐标系内的一条直线方程,那么这个式子求的就是从
内在
轴上方,直线下方的整点个数。
考虑
的时候,我们可以将式子分成两部分(整除的一部分可以提取出来):
.
这样一来
了,之后根据其几何意义,我们可以考虑将直线反转到
轴上,变成求
轴到,如图所示(草图,仅为方便理解):
这样经过反转以后,本来直线的斜率为
,此时变成了
,现在要求的整点个数,我们可以考虑从整个矩形减去
部分的整点。
为了方便表示,我们令
则有
因为之前
所以此时交换了
的位置,又回到了
的情况。
因为这样的形式类似于 GCD 的求解方式,所以也被称为类欧几里得。
剩下类欧几里得还有两种形式(推导方式方式类似,就不再赘述):