学习笔记--数论大杂烩 (看心情更新)

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

一、费马小定理:
假如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];
}
原文地址:https://www.cnblogs.com/DaD3zZ-Beyonder/p/5346204.html