1、gcd(o(logn))
注:用于求a,b的最大公约数
int gcd(int a,int b){return b==0?a:gcd(b,a%b);}
2、extend_gcd(o(logn)):
注:用于求出a,b的最大公约数,且求出x,y满足:ax+by=gcd(a,b);
具体证明过程:https://blog.csdn.net/Mr_Kingk/article/details/103448975
int extend_gcd(int a,int b,int &x,int &y)
{
if(b==0){
x=1;y=0;
return a;
}
else{
int result=extend_gcd(b,a%b,y,x);
y-=x*(a/b);
return result;
}
}
3、 素数筛选法:
时间复杂度o(n)
primes[]中依次存1~n中所有素数,vis[i]=true说明i不是素数,vis[i]=false说明i是素数
void get_primes(int n)
{
for(int i=2;i<=n;i++){
if(!vis[i]) primes[cnt++]=i;
for(int j=0;primes[j]*i<=n;j++){
vis[primes[j]*i]=true;
if(i%primes[j]==0) break;
}
}
}
4、得到质因子p在n!中的幂次(即:1-n这n个数中p的倍数的个数)o(n/ogn):
int get_num(int p,int n)//返回n!中因子p的个数=n/p+n/p^2+n/p^3+...
{
int s=0;
while(n) s+=n/p,n/=p;
return s;
}
5、分解质因子(将a分解成质数乘积的形式):
for(int i=2;i<=a;i++){//分解质因子模板(将a分解成所有质因子之积的形式)
int cnt=0;//cnt记录当前质因子个数
while(a%i==0){//i为a的一个质因子
a/=i;
cnt++;
}
}
6、 约数个数&&约数之和:
一个数可以分解为其素因子之积的形式,即:p1^c1*p2^c2*...*pn^cn
其约数个数为:(c1+1)*(c2+1)*..*(cn+1)
约数之和为:(p1^0+p1^1+..p1^c1)*(p2^0+p2^1+..p2^c2)*...*(pn^0+pn^1+..pn^cn),(因为将该式展开每一项即为一个约数)
用sum(p,k)表示个数为k的质因子p的那一括号的多项式(如:sum(p1,k1)=(p1^0+p1^1+.. +p1^k1)),默认k为奇数,
sum(p,k) =(p^0+p^1+.. +p^k)
=(p^0+p^1+...+p^(k/2))+(p^(k/2+1)+...+p^(k))
=(p^0+p^1+...+p^(k/2))+(p^(k/2+1)*(p^0+p^1+...+p^(k/2)))(因为k/2为向下取整,所以(k/2+1)+k/2=k)
= (1+p^(k/2+1))*(p^0+p^1+...p^(k/2))
= (1+p^(k/2+1))*sum(p,k/2) (这样就利用分治将问题规模缩小了一半)
int qmi(int x,int y)//快速幂
{
x%=mod;
int res=1;
while(y){
if(y&1) res=res*x%mod;
x=x*x%mod;
y>>=1;
}
return res;
}
int sum(int p,int k)
{
if(k==0) return 1;//p^0=1
if(k%2==0) return (p%mod*sum(p,k-1)+1)%mod;//k为偶数,则算后边k-1项p*(p^0+...+p^k-1)=(p^1+..+p^k),然后再加上p^0=1
return ((1+qmi(p,k/2+1))*sum(p,k/2))%mod;//k为奇数,则直接代入公式
}
7、 a^b&&64位整数乘法(快速幂:o(logn))
具体说明:https://blog.csdn.net/Mr_Kingk/article/details/104224169
(1)快速幂qpow:
int qpow(int a,int b,int p)
{
int ans=1%p;//一开始%p,防止b=0的情况(此时a^b=a^0=1,ans=a^b%p=1%p)
while(b){
if(b&1) ans=ans*a%p;//b&1(b与1)的结果为1时,即:当前被除数是1,余数(基数)就为1,此时乘上位权
a=a*a%p;//位权每次平方,作为下一次的位权
b>>=1;//被除数每次除2,将幂次转化成二进制
}
return ans;
}
(2) 64位整数乘法qmul:
int qmul(int a,int b,int p)
{
int ans=0;
while(b){
if(b&1) ans=(ans+a)%p;
a<<=1;
a%=p;
b>>=1;
}
return ans;
}
8、 高精度( 大数*数&&大数/数&&max(大数,大数) )
https://blog.csdn.net/Mr_Kingk/article/details/104375888
9、数的进制转换(任意两个进制之间的转换)
https://blog.csdn.net/Mr_Kingk/article/details/104433823
10、欧拉函数
用筛法求欧拉函数的phi数组(phi[n]:小于等于n的与n互质的数的个数):
void euler(int n)
{
for(int i=2;i<=n;i++) phi[i]=i;
for(int i=2;i<=n;i++){
if(phi[i]==i){
for(int j=i;j<=n;j+=i){//把质数的倍数筛掉,让其按公式运算
phi[j]=phi[j]/i*(i-1);
}
}
}
}
单个数的欧拉函数(小于等于n的与n互质的数的个数):
int get_euler(int x)
{
int res=x;
for(int i=2;i*i<=x;i++){
if(x%i==0){
res=res/i*(i-1);
while(x%i==0) x/=i;
}
}
if(x>1) res=res/x*(x-1);
return res;
}
11、矩阵乘法&&快速幂:
void mul(int res[][maxn],int a[][maxn],int b[][maxn])//res=a*b
{
int t[maxn][maxn];
memset(t,0,sizeof t);
for(int i=0;i<=n;i++){
for(int j=0;j<=n;j++){
for(int k=0;k<=n;k++){
t[i][j]=(t[i][j]+(a[i][k]*b[k][j]%mod))%mod;
}
}
}
memcpy(res,t,sizeof t);
}
void qpow(int a[][maxn],int t,int res[][maxn])//res=a^t
{
for(int i=0;i<=n;i++) res[i][i]=1;//一开始,res为单位矩阵E,即:A*E=A
while(t){
if(t&1) mul(res,res,mp);//res=res*mp
mul(mp,mp,mp);//mp=mp*mp
t>>=1;
}
}
计算fib数:https://blog.csdn.net/Mr_Kingk/article/details/105498491
12、二项式定理&&逆元&&费马小定理:
(1)二项式定理:
其中(x^n)*(y^m)的系数为:
(2)逆元:
形如:A*x%p=1%p (表示A关于p的逆元x)
(3)费马小定理:
a^(p-1)%p=1%p, (p为素数)
(4)费马小定理和逆元的推论:
a^(p-1)%p=1%p, (p为素数)
所以:(a^(p-2)*a)%p=1%p,这就转化成了A的逆元的形式,这里a相当于A,a^(p-2)就是a关于p的逆元
应用:https://blog.csdn.net/Mr_Kingk/article/details/105588140
13、组合数
一般来说,可以预处理出阶乘和阶乘的逆元,然后直接带入公式计算:
void init()//预处理除阶乘和2的i次幂
{
p[0]=fac[0]=1;
for(int i=1;i<maxn;i++){
fac[i]=fac[i-1]*i%mod;
p[i]=p[i-1]*2%mod;
}
}
int qpow(int x,int y)//快速幂
{
int res=1%mod;
while(y){
if(y&1) res=res*x%mod;
x=x*x%mod;
y>>=1;
}
return res;
}
int C(int n,int m)//组合数
{
int res=1;
res=res*fac[n]%mod;
int inv=qpow(fac[m]*fac[n-m]%mod,mod-2)%mod;//fac[m]*fac[n-m]的逆元
return res*inv%mod;
}
有时因为m较大,所以不能预处理出阶乘和阶乘的逆元,而k较小,且C(m,k)只需求一次,所以可直接计算得到其结果;而C(k,i)则可以由C(k,i-1)推出:
即C(k,i) = k*(k-1)*(k-i+1)/i! = (k/1)*((k-1)/2)*..*(k-i+1/i)
而从后往前求可表示为:i/(k-i+1)*(i-1)/(k-i+1)*..*1/(k-i+1) i从k到1,
除以(k-i+1)可以转化为乘(k-i+1)的逆元: 即qpow((k-i+1),mod-2)
依次可以求出C(k,1) = i/(k-i+1) ,C(k,2) = i/(k-i+1)*((i-1)/(k-i+1) (即:C(k,2) = C(k,1)*(i-1)/(k-i+1) ) .....
int c=1;
for(int i=k;i>=1;i--){
c=c*i%mod*qpow(k-i+1,mod-2)%mod;//计算C(k,k-i+1)
}
14、除法分块
[1,n]的所有数的因子个数之和等价于1-n这n个因子的出现次数之和,即:
(n/i向下取整),即:i这个因子在1-n的因子中出现的次数为:n/i
在计算时,计算每个因子出现次数相同的连续区间对答案的贡献:当前次数*等于此次数的区间长度
int get_num(int n)//求[1,n]每个数的因子个数之和
{
int l=1,r,res=0;
while(l<=n){
r=n/(n/l);
res+=n/l*(r-l+1);
l=r+1;
}
return res;
}
如果是求[1,n]的所有数的因子之和,答案就变成了1-n这n个因子的出现次数*i的和:
n/i向下取整,即:每次加上i这个因子在1-n的因子中出现的次数*i
在计算时,计算每个因子出现次数相同的连续区间对答案的贡献:当前次数*因子构成的等差数列的和
int get_num(int n)//求[1,n]每个数的所有因子之和
{
int l=1,r,res=0;
while(l<=n){
r=n/(n/l);
res+=n/l*(l+r)*(r-l+1)/2;
l=r+1;
}
return res;
}