快速幂的模板大家应该是不陌生的,之前我一直是直接记模板的,今天来具体解释一下快速幂模板的意义。
不取模的模板如下:(取模自己修改一下)
ll fp(ll a,ll b){ ll ans=1; ll base=a; while(b!=0){ if(b&1!=0) ans=ans*base; base*=base; b>>=1; } return ans; }
如何理解该模板:
首先快速幂的基本思想是分治和二进制,我们直接带入一个例子更好理解,如计算2的11次方。
首先ans=1,base=2,b=11;我们知道11的二进制是1011;if(b&1!=0) ans=ans*base;这一个语句的作用是,根据幂的二进制来决定是否要乘入答案,base*=base;的作用是让基数翻倍;
拿2的11次方来说,①b=1011,ans=1,base=2 ②ans=1*2,base=2*2,b=101, ③ans=2*2*2,base=2*2*2*2,b=10 ④ans=2*2*2,base=2*2*2*2*2*2*2*2,b=1 ⑤ ans=2*2*2*2*2*2*2*2*2*2*2,b=0 ⑥跳出for循环,结束。
这么理解一下,快速幂的模板再也不用四级硬背了,信手拈来。
由上可见,达到了计算2的11次方的目的,最坏的复杂度也只为O(logn),而暴力的复杂度是O(n),快速幂简直是完美情人!
(1)经典欧几里得算法(辗转相除法)一般用这个就够了
int gcd(int a,int b){ if(b==0) return a; else return gcd(b,a%b); }
如何理解记忆:
写题最讨厌的就是找代码模板了,所以我们要做到让这些常用代码印在心中!
其实就是两个数反复换位相互求余,当b为0时,a即为最大公因数。
如计算8和12的最大公因数,gcd(8,12),gcd(12,8),gcd(8,4),gcd(4,0)。
推导一遍就很容易记下。欧几里得算法依旧是复杂度为O(logn)的优秀算法。
(2)stein算法(如果不能A题,用这个改进一下)
针对欧几里德算法在对大整数进行运算时,需要试商导致增加运算时间的缺陷而提出的改进算法。
ll gcd(ll u,ll v) { if (u == 0) return v; if (v == 0) return u; if (~u & 1) { if (v & 1) return gcd(u >> 1, v); else return gcd(u >> 1, v >> 1) << 1; } if (~v & 1) return gcd(u, v >> 1); if (u > v) return gcd((u - v) >> 1, v); return gcd((v - u) >> 1, u); }
直接贴代码,因为也不常用,没有去理解,用得上的机会比较少。
求两个数的最小公倍数算法是基于gcd算法的基础上的。
int lcm(int a,int b){ return a/gcd(a,b)*b; }
如何理解记忆:
这个还是比较简单的,就是用一个数去除以最大公因数得到的结果,再乘以另一个数,就可以得到这两个数的最小公倍数。
(包括一,不包括本身)算法的解释都在代码里面了,比较好理解。
int factorSum(int n){ int sum=0; for (int i=1;i*i<=n;i++){//折半除,降低复杂度 if (n%i==0){ if (i==1||i*i==n) sum+=i;//如果这个因数为1或者刚好是该数的平方根,只加一个就可以了。 else sum+=i+n/i;//加上该因数和该因数的关联因数。 } } return sum; }
(1)一般情况下,简单的判断素数代码。
bool is_prime(int n){ if(n<=1) return false; for(int i=2;i*i<=n;i++) if(n%i==0) return false; return true; }
这个比较好理解,只要有除1以外的因数直接返回false,否则返回true
复杂度O(根号n),这个模板的时间复杂度对于10的12次方以内的数的判断是没有问题的
(2)更快速的方法
这两个方法在数据不是很大时对比不明显,数据很大时可以体现出方法2的优越性。代码如下:
bool isPrime(int a) { if(a == 1) return 0; if(a == 2 || a == 3) return 1; if(a%6 != 1 && a%6 != 5) return 0; int sqrtA = sqrt(a); for(int i = 5; i <= sqrtA; i += 6) { if(!(a%i)|| !(a%(i+2))) return 0; } return 1; }
如何理解记忆:
这个的理解过程就比较长了。【素数的判定-从暴力到高效】-C++ - 摸鱼酱 - 博客园 (cnblogs.com)
可以去博客园看一下这篇比较详细的博客,搬运一下。