zoukankan      html  css  js  c++  java
  • 学习笔记--数论大杂烩 (看心情更新)

    顺序有毒,请忽视。。。人傻,写的不好更忽视。。
    

    一、费马小定理:
    假如p是质数,且(a,p)=1,那么 a^(p-1)≡1(mod p)。即:假如a是整数,p是质数,且a,p互质(即两者只有一个公约数1),那么a的(p-1)次方除以p的余数恒等于1。
    证明略去,跟着这个网站证非常的舒服:http://www.xieguofang.cn/Maths/Number_Theory/Fermat%27s_Little_Theorem_1.htm#s2
    关于费马小定理的运用吧:
    在涉及取模运算的计算中,如果有除法,不能直接除以一个数,而应该变成乘以它的乘法逆元。
    当我们除以一个数n时,也就是乘上1/n,若x是1/n关于模N的逆元,则x=1/n (mod N),即 x*n=1(mod N)。由于我们做题时N常常为1000000007,而1000000007是个素数,所以它满足了费马小定理,而满足费马小定理说明解唯一,所以我们可以直接得出x*n=n^(N-1)。那么x=n^(N-2),即为1/n关于模N的乘法逆元。(总之就是求逆元
    可以用来检验一个数,是否是素数(其实然并卵)
    1、算出大数的余数 2、判断某种形状的素数个数 3、证明已知数是素数膜的原根 4、判断同余方程有解的必要条件。。
    可以用费马小定理来降幂

    二、欧拉函数φ:
    这里写图片描述
    欧拉函数的应用很多,尤其是上述加粗公式,很多问题在化简中所必需用到的公式,需要牢记。
    线性筛求欧拉函数模板:(利用积性函数的性质)

    long long prime[maxn],phi[maxn];
    bool flag[maxn]={0};
    int n;
    void get_phi()
    {
        memset(flag,0,sizeof(flag));
        flag[1]=1; 
        int cnt=0;
        for (int i=2; i<=maxn; i++)
            {
                if (!flag[i])
                    prime[cnt++]=i,phi[i]=i-1;
                for (int j=0; j<cnt && i*prime[j]<maxn; j++)
                    {
                        flag[i*prime[j]]=1;
                        if (i%prime[j]==0)
                            {
                                phi[i*prime[j]]=phi[i]*prime[j];
                                break;
                            }
                        else
                            phi[i*prime[j]]=phi[i]*(prime[j]-1);
                    }
            }
    }

    求单个欧拉函数

    int euler_phi(int n)//求单个欧拉函数
    {
        int m=(int)sqrt(n+0.5);
        int i,ans=n;
        for(i=2;i<=m;i++)
            if(n%i==0)
            {
                ans=ans/i*(i-1);
                while(n%i==0)n/=i;
            }
        if(n>1)ans=ans/n*(n-1);
        return ans;
    }

    三、高斯函数:
    即取整函数,在推公式中用处极大,变成中有现成的。。。
    这里写图片描述即下取整;
    这里写图片描述反之上取整;

    四、线性同余方程(拓展欧几里得算法):
    这里写图片描述a,b均为已知
    于是乎,解此类同余方程可以用到拓展欧几里得算法EXGCD:
    这里写图片描述

    #include<cstdio>
    using namespace std;
    void exgcd(long long a,long long b,long long &x,long long &y)
    {
        if(b==0)
            {
                x=1;y=0;return;
            }
        exgcd(b,a%b,x,y);
        long long t=x;
        x=y;
        y=t-(a/b)*y;
    }
    
    int main()
    {
        long long a,b;
        scanf("%lld%lld",&a,&b);
        long long x,y;
        exgcd(a,b,x,y);
        printf("%lld",(x+b)%b);
        return 0;
    }

    五、快速幂:

    long long quick_pow(int a,int b,int p)//返回a^b%p的值
    {
        long long re=1;
        long long tmp=a%p;
        while (b)
            {
                if (b&1)
                    re=(re*tmp)%p;
                tmp=(tmp*tmp)%p;
                b=b>>1;
            }
        return re;
    }

    六、逆元:
    这里写图片描述
    这里写图片描述
    除以一个数,等于乘上一个数的逆元,所以形如a/b%p就等价于a*b^-1(b的逆元)%p;(谁让除法有毒呢)
    拓展欧几里得法,求逆元:(基本万能喽)

    #include<iostream>
    #include<cstdio>
    using namespace std;
    int n,mod,x,y;
    void exgcd(int a, int b)
    {
        if(b==0)
        {
            x = 1;
            y = 0;
            return;
        }
        exgcd(b, a%b);
        int t = x;
        x = y;
        y = t - a/b*y;
    }
    
    
    int main()
    {
        //n在模mod意义下的逆元
        scanf("%d%d",&n,&mod);
        exgcd(n,mod);
        printf("%d
    ",(x%mod+mod)%mod);
        return 0;
    }

    安利两种线性求逆元:

        memset(flag,0,sizeof(flag));
        flag[1]=1; inv[1]=1;jc[1]=1;//inv为逆元,jc为阶乘
        int cnt=0;
        for (int i=2; i<=maxn; i++)
            {
                jc[i]=jc[i-1]*i%p;
                if (!flag[i])
                    prime[++cnt]=i,inv[i]=quick_pow(i,p-2,p);
                for (int j=1; j<=cnt && i*prime[j]<=maxn; j++)
                    {
                        flag[i*prime[j]]=1;
                        inv[i*prime[j]]=inv[i]*inv[prime[j]]%p;
                        if (i%prime[j]==0) break;
                    }
            }
        inv[1]=1;
        for (int i=2; i<=maxn&&i<p; i++)
            inv[i]=(p-p/i)*inv[p%i]%p;

    七、中国剩余定理:

    若要问我:你对“中国剩余定理”的态度是怎样的呢?回答只有两个字:“敬畏”。
    

    这里写图片描述

    int china(int *a,int *m,int n)
    {
       int M=1,ans=0,mi,i,x,y;
       for(i=0;i<n;i++) M*=m[i];
       for(i=0;i<n;i++)
       {
           mi=M/m[i];
           exgcd(m[i],mi,x,y);
           ans=(ans+mi*y*a[i])%M;
       }
       return (ans%M+M)%M;
    }

    中国剩余定理

    #include <iostream>
    #include<cstdio>
    
    using namespace std;
    
    int Extended_Euclid(int a,int b,int &x,int &y)    //扩展欧几里得算法
    {
        int d;
        if(b==0)
        {
            x=1;y=0;
            return a;
        }
        d=Extended_Euclid(b,a%b,y,x);
        y-=a/b*x;
        return d;
    }
    
    int Chinese_Remainder(int a[],int w[],int len)    //中国剩余定理  a[]存放余数  w[]存放两两互质的数
    {
        int i,d,x,y,m,n,ret;
        ret=0;
        n=1;
        for (i=0;i<len;i++)
            n*=w[i];
        for (i=0;i<len;i++)
        {
            m=n/w[i];
            d=Extended_Euclid(w[i],m,x,y);
            ret=(ret+y*m*a[i])%n;
        }
        return (n+ret%n)%n;
    }
    
    
    int main()
    {
        int n,i;
        int w[15],b[15],cs[15];
        while (scanf("%d",&n),n)   
        {
            for (i=0;i<n;i++)
            {
                scanf("%d%d%d",&cs[i],&w[i],&b[i]);
            }
            printf("%d
    ",Chinese_Remainder(b,w,n));
        }
        return 0;
    }

    八、质因数分解:

    对于i,从2到sqrt((double)m),如果m%i==0,则i是m的一个质因数, 
    然后m/i=m,i=2,重新判断,直至最后,剩下的那个数同样是一个质因数,要加上的.  
    int zhi(int n,int *x){   int count=0;   int i=2; 
      while(i<=(int)sqrt((double)n)){     if(n%i==0){ 
          x[count++]=i;       n=n/i;     }else{       i++;     }    } 
       x[count++]=n;    return count; 
    }//返回质因数的个数,数组x是所有的因子 
    
    //n是要分解的数,pn是100范围内质数的个数,prim是100范围内的所有质数,q存储分解出来的质数相应的int zhi_num(int n,int pn,int *prim,int *q) { 
        for (int j=0;j<pn&&prim[j]<=n;j++)     { 
            if (n%prim[j]==0)         { 
                __int64 temp=0; 
                while (n%prim[j]==0)             { 
                    temp++;//相应的prim[j]的个数                 n/=prim[j];             } 
                q[j]+=temp;         }     } }   
    
    prim[25]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97}; int q[25]={0}; int pn=25; 
    zhi_num(20,pn,prim,q);

    九、BSGS(Baby Steps Gaint Steps):
    安利一下算法流程:(不同于一般的BSGS)
    首先要求 a^x=b (mod c)
    假定x=im-j (m=ceil(sqrt(n)))
    那么进行变换:
    a^im = b*a^j (mod c)
    枚举b*a^j mod p的值存入哈希表(map)。
    再枚举a^im的值,从哈希表里找,如果能找到一样的答案就是i*m-j
    这里写图片描述
    具体模板详见题目;
    EXBSGS:拓展到c不为质数的情况
    这里写图片描述
    模板:

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<algorithm>
    #include<map>
    #include<cmath>
    using namespace std;
    long long gcd(long long a,long long b)
    {
        if (b==0) return a; else return gcd(b,a%b);
    }
    long long quick_pow(long long x,long long y,long long p)
    {
        long long re=1; x%=p; y%=p;
        for (long long i=y; i; i>>=1,x=x*x%p)
            if (i&1) re=re*x%p;
        return re;
    }
    long long bsgs(long long a,long long b,long long p){
        if(a%=p,b%=p,b==1)return 0;
        long long t=1;long long f,g,delta=0,m=sqrt(p)+1,i;
        for(g=gcd(a,p);g!=1;g=gcd(a,p)){
            if(b%g)return -1;
            b/=g,p/=g,t=t*(a/g)%p,delta++;
            if(b==t)return delta;
        }
        map<long long,long long>hash;
        for(i=0;i<m;i++,b=b*a%p)hash[b]=i;
        for(i=1,f=quick_pow(a,m,p);i<=m+1;i++)
        if(t=t*f%p,hash.count(t))return i*m-hash[t]+delta;
        return -1;
    }
    int main()
    {
        long long a,b,p;
        while (cin>>a>>b>>p)
            {
                //if (a==0 && b==0 && p==0) break;
                long long ans=bsgs(a,b,p);
                if (ans==-1) puts("No Solution");
                        else printf("%lld
    ",ans);
            }
        return 0;
    }

    十、Lucas定理(组合数取模):
    A、B是非负整数,p是质数。AB写成p进制:A=a[n]a[n-1]…a[0],B=b[n]b[n-1]…b[0]。
    则组合数C(A,B)与C(a[n],b[n])C(a[n-1],b[n-1])…*C(a[0],b[0]) modp同余
    即:Lucas(n,m,p)=c(n%p,m%p)*Lucas(n/p,m/p,p)
    证明略,可以看这里:http://blog.csdn.net/pi9nc/article/details/9615359
    实现:

    int quick_pow(long long a,int b,int p)//快速幂(算逆元)
    {
        int ans=1;
        for(int i=b;i;i>>=1,a=(a*a)%p)
            if(i&1)ans=(ans*a)%p;
        return ans;
    }
    void cs()//初始出阶乘
    {
        jc[1][0]=jc[2][0]=jc[3][0]=jc[0][0]=1;//第一维无意义(因题目分成的几个模质数)
        for (int i=0; i<4; i++)
            for (int j=1; j<=pp[i]; j++)
                jc[i][j]=(jc[i][j-1]*j)%pp[i];
    }
    int C(int n,int m,int p)//组合数
    {
        if (n<m) return 0;
        return jc[p][n]*quick_pow(jc[p][m]*jc[p][n-m],pp[p]-2,pp[p])%pp[p];
    }
    int lucas(int n,int m,int p)//lucas
    {
        if (m==0) return 1;
        return C(n%pp[p],m%pp[p],p)*lucas(n/pp[p],m/pp[p],p)%pp[p];
    }
  • 相关阅读:
    JAVA学习日记1-0706
    同步一个fork
    面试题 17.13. 恢复空格-7月9日
    3. 无重复字符的最长子串(leetcode)-7月8日
    面试题 16.11. 跳水板(leetcode)-7月8日
    112.路径总和(leetcode)-7月7日
    Git使用入门
    第一次尝试
    OpenPCDet: Open-MMLab 面向LiDAR点云表征的3D目标检测代码库
    人工智能和机器学习能为抗击新冠肺炎做些什么?
  • 原文地址:https://www.cnblogs.com/DaD3zZ-Beyonder/p/5346204.html
Copyright © 2011-2022 走看看