数论基础模板

壹——最大公约数

  欧几里得算法:

  gcd(x,y)=gcd(y,x%y);

  边界条件:if(y==0)return x;

#include<bits/stdc++.h>
using namespace std;
int a,b;
inline int gcd(int x,int y)
{
    if(!y)return x;
    else return gcd(y,x%y);
}
int main()
{
        cin>>a>>b;
    cout<<gcd(a,b)<<endl;
    return 0;
}

 贰——欧拉函数

  定义法:

  

#include<bits/stdc++.h>
using namespace std;
int ol(int x)
{
        int y=x;
        for(int j=2;j<=x/j;j++)
            if(x%j==0)
            {
                y=y/j*(j-1);
                while(x%j==0)x/=j;
            }
        if(x>1)y=y/x*(x-1);
        return y;
}       
int main()
{
        int x;
        scanf("%d",&x);
        printf("%d\n",ol(x));
    return 0;
}

   筛法:

#include<bits/stdc++.h>
#define N 1000010
#define ll long long
using namespace std;
ll phi[N],pr[N];
bool st[N];
int n,k;
void ol(int x)
{
    phi[1]=1;
    for(int i=2;i<=x;i++)
    {
        if(st[i]==0)
        {
            phi[i]=i-1;
            pr[k++]=i;
        }
        for(int j=0;pr[j]<=x/i;j++)
        {
            st[pr[j]*i]=1;
            if(i%pr[j]==0)
            {
                phi[i*pr[j]]=pr[j]*phi[i];
                break;
            }
            phi[i*pr[j]]=(pr[j]-1)*phi[i];
        }
    }
    ll ans=0;
    for(int i=1;i<=x;i++)ans+=phi[i];
    printf("%lld",ans);
}
int main()
{
    scanf("%d",&n);
    ol(n);
    return 0;
}

 

叁——快速幂

 

#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll ksm(ll x,ll y,ll MOD)
{
    ll sum=1;
    while(y)
    {
        if(y%2)
        {
            sum=sum*x%MOD;
        }
        x=x*x%MOD;
        y/=2;
    }
    return sum;
}
int main()
{
    ll a,b,mod;
    int k;
    cin>>k;
    cin>>a>>b>>mod;
    cout<<ksm(a,b,mod)<<endl;
    return 0;
}

 

 

肆——逆元

  当n为质数时,可以用快速幂求逆元:  

 

            a/b ≡ a * x (mod n)

  两边同乘b可得   a ≡ a * b * x (mod n)
        即   b * x ≡ 1 (mod n)
  由费马小定理可知,当n为质数时
  b ^ (n - 1) ≡ 1 (mod n)
  b * b ^ (n - 2) ≡ 1 (mod n)

#include <iostream>
using namespace std;
typedef long long LL;

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

int main()
{int a, p;
  cin >> a >> p;
  if(a % p == 0) puts("impossible");
  else cout << qmi(a, p - 2, p) << endl;
 return 0; }

  

  当n不是质数时,可以用扩展欧几里得算法求逆元:

  a有逆元的充要条件是a与p互质,所以gcd(a, p) = 1
  假设a的逆元为x,那么有a * x ≡ 1 (mod p)
  等价:ax + py = 1

#include <iostream>
using namespace std;
typedef long long LL;
int n;

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


int main()
{
    cin >> n;int a, p, x, y;
    cin >>  a >> p;
    int d = exgcd(a, p, x, y);
    if (d == 1) cout << ((LL)x + p) % p << endl;
    else puts("impossible");
    return 0;
}

   线性逆元

 

 

 

#include<bits/stdc++.h> 
#define N 1000000
#define MOD 9901
using namespace std;
int ny[N];
int main()
{
    int ny[1]=1;
    int n;
    cin>>n;
    for(int i=2;i<=n;i++)
        ny[i]=-MOD/i*ny[p%i]%MOD;
    return 0;
}

 


伍—— 扩展欧几里得

  用于求解方程 ax+by=gcd(a,b)的解  

  当 b=0时:ax+by=a 故而 x=1,y=0

  当 b≠0时:因为gcd(a,b)=gcd(b,a%b)
          而 bx′+(a%b)y′=gcd(b,a%b)
            bx′+(a−⌊a/b⌋∗b)y′=gcd(b,a%b)
            ay′+b(x′−⌊a/b⌋∗y′)=gcd(b,a%b)=gcd(a,b)
          故而x=y′,y=x′−⌊a/b⌋∗y′

  因此可以采取递归算法 先求出下一层的x′和y′再利用上述公式回代即可

  

#include<bits/stdc++.h>
using namespace std;
int n;
int x, y;
inline int exgcd(int a,int b)
{
    if(b==0)
    {
        x=1,y=0;
        return a;
    }
    else
    {
        int gcd=exgcd(b,a%b);
        int z=x;
        x=y;
        y=z-a/b*y;
        return gcd;
    }
}
int main()
{
    int a,b;
    scanf("%d%d",&a,&b);
    exgcd(a,b);
    printf("%d %d\n",x,y);
    return 0;
}

  求解一般一般的方程 ax+by=c 

  设 d=gcd(a,b)
  则其有解当且仅当 d|c
  求解方法如下:用扩展欧几里得求出 ax0+by0=d的解

  则a(x0∗c/d)+b(y0∗c/d)=c
  故而特解为 x'=x0∗c/d,y'=y0∗c/d
  而 通解=特解+齐次解
  而 齐次解即为方程 ax+by=0的解
  故而通解为x=x'+k∗b/d,y=y'−k∗a/d(k∈Z)

 

  一次同余方程:ax≡b(modm)

 

           ax=m∗(−y)+b

           ax+my=b

  

#include<bits/stdc++.h>
#define ll long long
using namespace std;
int n;
ll x,y;
inline ll exgcd(ll a,ll b)
{
    if(b==0)
    {
        x=1,y=0;
        return a;
    }
    else 
    {
        int gcd=exgcd(b,a%b);
        int z=x;
        x=y,y=z-a/b*y;
        return gcd;
    }
}

int main()
{
    cin>>n;
    ll a,b,m;
    scanf("%lld %lld %lld",&a,&b,&m);
    int d=exgcd(a,m);
    if(b%d==0)
    {
        ll t=b/d;
        printf("%lld\n",(x*t+m)%m);
    }
    else
        puts("impossible");
    }
    return 0;
}

 陆——线行筛法

#include<bits/stdc++.h>
#define N 1000000
using namespace std;
int pir[N],vis[N],cut;
void xian()
{
    int n=N;
    for(int i=2;i<=n;i++)
    {
        if(pir[i]==0)vis[++cut]=i,pir[i]=i;
        for(int j=1;j<=cut;j++)
        {
            if(vis[j]>pir[i]||vis[j]>n/i)break;
             pir[i*vis[j]]=vis[j];
             
        }
    }
}
int main()
{
    xian();
    return 0;       
}

 

posted @ 2020-12-29 13:09  君与  阅读(59)  评论(0编辑  收藏  举报