数论模板

本文详细介绍了数论中的一些基本算法模板,包括gcd、extend_gcd、素数筛选、欧拉函数,以及快速幂、高精度计算、数的进制转换等,并提供了相关链接深入探讨各种算法的应用和实现细节。
摘要由CSDN通过智能技术生成

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个因子的出现次数之和,即:

\sum_{i=1}^{n}n/i  (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的和:

\sum_{i=1}^{n}(n/i)*in/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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值