数论

1.质数

1.质数的判定:

bool fun(int n){
    for(int i=2;i<=n/i;i++)
        if(n/i==0) return false;
    return true;
}

枚举到n/i既可以优化速度也可以有效防止溢出。
2.分解质因数:
一个数n最多有一个质因数大于sqrt(n)

void fun(int n){
    for(int i=2;i<=n/i;i++){//枚举2到sqrt(n);
        if(n%i==0){//i是质数;
            int cnt=0;
            while(n%i==0){
                n/=i;
                cnt++;
            }
            cout<<i<<' '<<cnt<<endl;
        }
    }
    if(n>1) cout<<n<<' '<<1<<endl;//(特殊情况);
}

这里出现的只会是素数,因为当把一个素数除干净的时候,就不会出现以这个素数为因数的合数作为约数。
3.筛质数
基本思路: 在2-n之中,i从2开始枚举,删除掉i的倍数,剩下的就是质数;
埃氏筛法:

int a[N];
bool st[N];
int cnt;
void fun(int n){
    for(int i=2;i<=n;i++){
        if(!st[i]){ 
            a[cnt++]=i;
            for(int j=i+i;j<=n;j+=i)
                st[j]=true;
        }
    }
}

线性筛,核心:用最小质因数删去所有的合数,并且保证只删除一次;

int a[N];
bool st[N];
int fun(int n){
    int cnt=0;
    for(int i=2;i<=n;i++){
        if(!st[i]) a[cnt++]=i;
        for(int j=0;a[j]<=n/i;j++){
            st[a[j]*i]=true;
            if(i%a[j]==0) break;
        }
    }
    return cnt;
}

求约数的个数;
先将n分解质因数,其中假如一个质因数为,p^a,每个质因数都可以都有0~a种选法,所以n的约数个数为每个质因数的次数+1累乘的结果;

//朴素写法
int fun(int n){
    int res=1;
    for(int i=2;i<=n/i;i++){
        if(n%i==0){
            int s=0;
            while(n%i==0){
                s++;
                n/=i;
            }
            res=res*(s+1)%MOD;
        }
    }
    if(n>1) res=res*2%MOD;
    return res;
}
//哈希表写法;
int fun1(int n){
    unordered_map<int,int>a;
    int res=1;
    for(int i=2;i<=n/i;i++){
        while(n%i==0){
            a[i]++;
            n/=i;
        }
    }
    if(n>1) a[n]++;
    for(auto i:a) res=res*(i.second+1)%MOD;
    return res;
}

2,求约数的和:
仍然是分解质因数,
约数之和就是分解出的每个约数(p^a),然后在进行质因数分解,
最后相乘即可;
约数之和: (p10+p11+…+p1c1)∗…∗(pk0+pk1+…+pkck)

int fun(int n){
    unordered_map<int,int>s;
    for(int i=2;i<=n/i;i++){//分解质因数;
        while(n%i==0){
            s[i]++;
            n/=i;
        }
    }
    if(n>1) s[n]++;
    int ans=1;
    for(auto i:s){
        int t=1;
        int p=s.first,a=s.second;
        while(a--) t=t*p+1;//每个约数求和
        ans=ans*t;//累乘;
    }
    return ans;
}

3.欧拉函数:
欧拉函数就是得到1~n之中和n互质的数的个数;
其中p为质因数;
公式:ϕ(N) = N∗(p1−1/p1)∗(p2−1/p2)∗…∗(pm−1/pm)

int fun(int n){
    int res=n;
    for(int i=2;i<=n/i;i++){
        if(n%i==0){
            res=res/i*(i-1);
            while(n%i==0) n/=i;
        }
    }
    if(n>1) res=res/n*(n-1);
    return res;
}

3.线性筛求欧拉函数;
套用线性筛的模板即可;

int a[N],cnt;
bool st[N];
int phi[N];
void fun(int n){
    phi[1]=1;
    for(int i=2;i<=n;i++){
        if(!st[i]) a[cnt++]=i,phi[i]=i-1;
        for(int j=0;a[j]<=n/i;j++){
            st[a[j]*i] =true;
            if(i%a[j]==0){
                phi[a[j]*i]=phi[i]*a[j];
                break;
            }
            phi[a[j]*i]=phi[i]*(a[j]-1);
        }
    }
}
详解:当i是质数的时候,他的互质数的个数就是1~i-1,phi[i]=i-1;
当i%a[j]=0时,说明i的最小质因数是a[j],
同时i*a[j]的最小质因数也是a[j],
*//a[j]*i的质因子其实是和i的质因子时相同的,只是多乘了一个a[j],不会出现
//其他的质因子;*
计算i*a[j]的欧拉函数时,因为在计算i的欧拉函数时已经计算过一次phi[i],
所以i*a[j]的欧拉函数就是a[j]*phi[i];
当i%a[j]!=0时,说明a[j]比i之前的数的最小质因数,
所以计算phi[a[j]*i]=phi[i]*a[j]*(1-1/a[j]),
化简可得:phi[a[j]*i]=phi[i]*(a[j]-1);

4.快速幂;
求a^b%p的结果;
可以把b转化成二进制,当算b的二进制的某一位的时候,如果是1,就相乘,a每次都要跟自己进行平方运算;

int fun(int a,int b,int p){
    int res=1;
    while(b){
        if(b&1) res=(ll) res*a%p;
        a=(ll)a*a%p;
        b>>=1;
    }
    return res;
}

附加题:快速幂

#include<iostream>
#include<algorithm>
#include<unordered_map>
#include<cmath>
using namespace std;
typedef long long ll;
int fun(int a,int b,int p){
    int res=1;
    while(b){
        if(b&1) res=(ll)res*a%p;
        a=(ll)a*a%p;
        b>>=1;
    }
    return res;
}

int main(){
     int n;
    cin>>n;
    unordered_map<int,int>a;
    for(int i=2;i<=n;i++){
        int k=i;
        for(int j=2;j<=k/j;j++){

            while(k%j==0){
                a[j]++;
                k/=j;
            }
        }
        if(k>1) a[k]++;
    }
    int res=1;
    a[2]-=a[5];

    for(auto i:a){
        int p=i.first,k=i.second;

        if(p==5) continue;
        int o=fun(p,k,10);
        res=res*o%10;
    }
    cout<<res<<endl;
    return 0;
}

有关数论的一些知识:
同余:假如a mod m=b mod m,称为a与b 对m取模同余;
费马定理:假如p是质数,ap对p取模同余a;

例子:乘法逆元:
若整数b,m互质,并且对于任意的整数 a,如果满足b|a,则存在一个整数x,使得a/b≡a∗x(mod m),则称x为b的模m乘法逆元,记为b−1(mod m)。
b存在乘法逆元的充要条件是b与模数m互质。当模数m为质数时,bm−2即为b的乘法逆元。

这里给a/b≡a∗x(mod m)同时成b,再将a除过去就可以得到,bx对m取模同余1,有根据费马定理bp, , bx≡bm-1(mod m),x=bm-2;
欧拉定理若a与n互质,则aphi(n)mod n=1;
欧几里得算法:
一般用于求最大公约数,直接__gcd也可hh。
推导过程:
一般情况下假如一个整数d可以被x整除,也可以被y整除,那么它可以被ax+by整除;ab为系数;
gcd(a,b)=gcd(b,a mod b)
因为a mod b=a-floor(a/b)*b,所以a mod b也可以被d整除;
可以推出:gcd(a,b)=gcd(b,a mod b);

int gcd(int a,int b){
    return b? gcd(b,a%b):a;
}

扩展欧几里得算法:
找到一对系数xy满足,ax+by=gcd(a,b);
过程:
令n=gcd(a,b);所以gcd(b,a%b)=n;即by+a%bx=n;即
by+(a-(floor)a/bb)x=n;
合并同类项得:ax+b(y-a/bx)=n;
所以x没变,y变成(y-a/b
x);

int gcd(int a,int b,int &x,int &y){
    if(b==0){
        x=1,y=1;
        return a;
    }
    int d=gcd(b,a%b,y,x);
    y=y-a/b*x;
    return d;
}

应用:线性同余方程;
给定n组数据ai,bi,mi,对于每组数求出一个xi,使其满足ai∗xi≡bi(mod mi),如果无解则输出impossible。
链接:线性同余方程
题解:方程可以转化为:ax=my+b;即ax-my=b;
其中b一定时gcd(a,m)的倍数;
最后找到得出x,再给x乘b/d就好;

#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
int gcd(int a,int b,int &x,int &y){
    if(b==0){
        x=1,y=1;
        return a;
    }
    int d=gcd(b,a%b,y,x);
    y-=a/b*x;
    return d;
}
int main(){
    int t;
    cin>>t;
    int a,b,m;
    while(t--){
        scanf("%d%d%d",&a,&b,&m);
        int x,y;
        int d=gcd(a,m,x,y);
        if(b%d) puts("impossible");
        else x=(ll)x*b/d%m,printf("%d\n",x);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值