数论知识学习总结(二)

文章目录

  • 一、欧拉函数
    • 1.欧拉函数
    • 2.筛法求欧拉函数(采用筛质数的线性筛法)
    • 二、快速幂
    • 1.快速幂
    • 2.快速幂求逆元
  • 三、扩展欧几里得算法
    • 1.扩展欧几里得算法
    • 2.线性同余方程
  • 四、中国剩余定理
    • 1.表达整数的奇怪方式


一、欧拉函数

在数论,对正整数 n n n,欧拉函数是小于等于 n n n的正整数中与 n n n互质的数的数目.

1.欧拉函数

1 ∼ N 1 \sim N 1N 中与 N N N 互质的数的个数被称为欧拉函数,记为 ϕ ( N ) \phi(N) ϕ(N)
若在算数基本定理中, N = p 1 a 1 p 2 a 2 … p m a m N=p_{1}^{a_{1}} p_{2}^{a_{2}} \ldots p_{m}^{a_{m}} N=p1a1p2a2pmam ,则: ϕ ( N ) = N × p 1 − 1 p 1 × p 2 − 1 p 2 × … × p m − 1 p m \phi(N)=N \times \frac{p_{1}-1}{p_{1}} \times \frac{p_{2}-1}{p_{2}} \times \ldots \times \frac{p_{m}-1}{p_{m}} ϕ(N)=N×p1p11×p2p21××pmpm1
其中 p 1 p_1 p1~ p m p_m pm m m m个互不相同的质数。
证明:
可以由容斥原理证明。

  1. 删去 p 1 , p 2 , . . . , p m p_1,p_2,...,p_m p1,p2,...,pm的倍数
  2. 加上 p 1 p 2 , p 1 p 3 , . . . p_1p_2,p_1p_3,... p1p2,p1p3,...的倍数

ϕ ( n ) = N − ( N p 1 + N p 2 + . . . N p m ) + ( N p 1 p 2 + N p 1 p 3 + . . . ) − ( N p 1 p 2 p 3 + N p 1 p 2 p 4 + . . . ) + . . . \phi(n)=N-(\frac{N}{p_1}+\frac{N}{p_2}+...\frac{N}{p_m})+(\frac{N}{p_1p_2}+\frac{N}{p_1p_3}+...)-(\frac{N}{p_1p_2p_3}+\frac{N}{p_1p_2p_4}+...)+... ϕ(n)=N(p1N+p2N+...pmN)+(p1p2N+p1p3N+...)(p1p2p3N+p1p2p4N+...)+...
整理一下可得: ϕ ( N ) = N × p 1 − 1 p 1 × p 2 − 1 p 2 × … × p m − 1 p m \phi(N)=N \times \frac{p_{1}-1}{p_{1}} \times \frac{p_{2}-1}{p_{2}} \times \ldots \times \frac{p_{m}-1}{p_{m}} ϕ(N)=N×p1p11×p2p21××pmpm1
ex:
ϕ ( 6 ) = 6 ∗ 1 2 ∗ 2 3 = 2 \phi(6)=6*\frac{1}{2}*\frac{2}{3}=2 ϕ(6)=62132=2


相关题目:AcWing 873. 欧拉函数

#include 

using namespace std;

int main()
{
    int n;
    scanf("%d", &n);
    while(n --)
    {
        int x;
        scanf("%d", &x);
        int phi = x;
        for(int i = 2; i <= x / i; i ++ )
        {
            if(x % i == 0)
            {
                phi = phi / i * (i - 1);
                while(x % i == 0) x /= i;
            }
        }
        if(x > 1) phi = phi / x * (x - 1);
        printf("%d\n", phi);
    }
    
    return 0;
}

2.筛法求欧拉函数(采用筛质数的线性筛法)

p j p_j pj是1~ i i i中的一个质数,对于一个欧拉函数 ϕ ( i ) \phi(i) ϕ(i)有如下性质:

  1. i i i为一个质数,则1~ i i i中一定有 ϕ ( i ) = i − 1 \phi(i) = i -1 ϕ(i)=i1

  2. p j p_j pj不是 i i i的最小质因子,可得 i i i的最小质因子一定比 p j p_j pj大,所以有: ϕ ( i ) = i × p 1 − 1 p 1 × p 2 − 1 p 2 × … × p m − 1 p m (1) \phi(i)=i \times \frac{p_{1}-1}{p_{1}} \times \frac{p_{2}-1}{p_{2}} \times \ldots \times \frac{p_{m}-1}{p_{m}}\tag1 ϕ(i)=i×p1p11×p2p21××pmpm1(1)
    p j p_j pj一定是 i × p j i\times p_{j} i×pj的最小质因子,有: ϕ ( i × p j ) = i × p j × p j − 1 p j × p 1 − 1 p 1 × p 2 − 1 p 2 × … × p m − 1 p m (2) \phi(i\times p_j)=i \times p_j \times \frac{p_j-1} {p_j} \times \frac{p_{1}-1}{p_{1}} \times \frac{p_{2}-1}{p_{2}} \times \ldots \times \frac{p_{m}-1}{p_{m}}\tag2 ϕ(i×pj)=i×pj×pjpj1×p1p11×p2p21××pmpm1(2)
    将(1)带入(2)中有:
    ϕ ( i × p j ) = ϕ ( i ) × ( p j − 1 ) \phi(i \times p_j)=\phi(i) \times (p_j-1) ϕ(i×pj)=ϕ(i)×(pj1)

  3. p j p_j pj i i i的最小质因子,所以有: ϕ ( i ) = i × p 1 − 1 p 1 × p 2 − 1 p 2 × … × p j − 1 p j × … × p m − 1 p m (1) \phi(i)=i \times \frac{p_{1}-1}{p_{1}} \times \frac{p_{2}-1}{p_{2}} \times \ldots \times\frac{p_j-1} {p_j} \times \ldots \times \frac{p_{m}-1}{p_{m}}\tag1 ϕ(i)=i×p1p11×p2p21××pjpj1××pmpm1(1)
    p j p_j pj一定是 i × p j i\times p_{j} i×pj的最小质因子,有: ϕ ( i × p j ) = i × p j × p 1 − 1 p 1 × p 2 − 1 p 2 × … × p j − 1 p j × … × p m − 1 p m (2) \phi(i\times p_j)=i \times p_j \times \frac{p_{1}-1}{p_{1}} \times \frac{p_{2}-1}{p_{2}} \times \ldots \times\frac{p_j-1} {p_j} \times \ldots \times \frac{p_{m}-1}{p_{m}}\tag2 ϕ(i×pj)=i×pj×p1p11×p2p21××pjpj1××pmpm1(2)
    将(1)带入(2)中有:
    ϕ ( i × p j ) = p j × ϕ ( i ) \phi(i \times p_j)=p_j \times \phi(i) ϕ(i×pj)=pj×ϕ(i)

相关题目:Acwing 874. 筛法求欧拉函数

#include 

using namespace std;

typedef long long LL;

const int N = 1e6 + 10;

int primes[N], cnt;
int phi[N];
bool st[N];

int main()
{
    int n;
    scanf("%d", &n);
    phi[1] = 1;
    for(int i = 2; i <= n; i ++ )
    {
        if(!st[i])
        {
            phi[i] = i - 1;
            primes[cnt ++ ] = i;
        }
        for(int j = 0; primes[j] <= n / i; j ++ )
        {
            int t = i * primes[j];
            st[t] = true;
            if(i % primes[j] == 0)
            {
                phi[t] = phi[i] * primes[j];
                break;
            }
            phi[t] = phi[i] * (primes[j] - 1);
        }
    }
    LL res = 0;
    for(int i = 1; i <= n; i ++ ) res += phi[i];
    printf("%lld\n", res);
    
    return 0;
}

二、快速幂

1.快速幂

对于求 a k a^k ak,朴素做法是将 a a a累乘 k k k次,其时间复杂度为 O ( n ) O(n) O(n)!有一种特别求幂的方法可以将时间复杂度降低到 O ( l o g n ) O(logn) O(logn)
做法:

  1. 先预处理 a 2 0 , a 2 1 , a 2 2 , . . . , a 2 l o g k a^{2^{0}},a^{2^{1}},a^{2^{2}},...,a^{2^{logk}} a20,a21,a22,...,a2logk,花费的时间为: l o g k logk logk
  2. 其中 a 2 1 = ( a 2 0 ) 2 , a 2 2 = ( a 2 1 ) 2 , . . . . . . , a 2 l o g k = ( a 2 l o g k − 1 ) 2 a^{2^{1}}=(a^{2^{0}})^2,a^{2^{2}}=(a^{2^{1}})^2,......,a^{2^{logk}}=(a^{2^{logk}-1})^2 a21=(a20)2,a22=(a21)2,......,a2logk=(a2logk1)2,结论每一个 a a a都可以写成上一个 a a a的平方!
  3. 可以将 a k a^k ak拆成 a 2 x 1 × a 2 x 2 × a 2 x 3 × . . . . . . × a 2 x t a^{2^{x_1}} \times a^{2^{x_2}} \times a^{2^{x_3}} \times ...... \times a^{2^{x_t}} a2x1×a2x2×a2x3×......×a2xt
  4. a k = a 2 x 1 + 2 x 2 + . . . . . . + 2 x t a^k =a^{2^{x_1} + 2^{x_2}+......+2^{x_t}} ak=a2x1+2x2+......+2xt,即 k = 2 x 1 + 2 x 2 + . . . . . . + 2 x t k = 2^{x_1} + 2^{x_2}+......+2^{x_t} k=2x1+2x2+......+2xt,为 k k k的二进制数的表示!

相关题目:AcWing 875. 快速幂

#include 

using namespace std;

typedef long long LL;

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

int main()
{
    int n;
    scanf("%d", &n);
    while(n -- )
    {
        int a, b, p;
        scanf("%d%d%d", &a, &b, &p);
        printf("%lld\n", qmi(a, b, p));
    }
    
    return 0;
}

时间复杂度: O ( l o g n ) O(logn) O(logn)

2.快速幂求逆元

乘法逆元的定义:
若整数 b , m b, m b,m 互质,并且对于任意的整数 a a a ,如果满足 b ∣ a b \mid a ba ,则存在一个整数 x x x ,使得 a / b ≡ a × x (   m o d   m ) a / b \equiv a \times x(\bmod m) a/ba×x(modm) ,则称 x x x b b b 的模 m m m 乘法逆元,记为 b − 1 (   m o d   m ) b^{-1}(\bmod m) b1(modm) b b b 存在乘法逆元的充要条件是 b b b 与模数 m m m 互质。当模数 m m m 为质数时, b m − 2 b^{m-2} bm2 即为 b b b 的乘法逆元。
欧拉定理:
a , m ∈ N + a, m \in N^{+} a,mN+,且 gcd ⁡ ( a , m ) = 1 \operatorname{gcd}(a, m)=1 gcd(a,m)=1 ,则我们有:
a φ ( m ) ≡ 1 (   m o d   m ) a^{\varphi(m)} \equiv 1(\bmod m) aφ(m)1(modm)
小费马定理:
如果 p p p是一个质数,而整数 a a a不是 p p p的倍数,则有 a p − 1 ≡ 1 ( m o d   p ) a^{p-1}≡1(mod\ p) ap11(mod p)
证明:可以由欧拉定理求得, p p p是一个质数,则有 ϕ ( p ) = p − 1 \phi(p)=p-1 ϕ(p)=p1,即可得到 a p − 1 ≡ 1 ( m o d   p ) a^{p-1} \equiv 1(mod \ p) ap11(mod p)
相关题目链接:AcWing 876. 快速幂求逆元

#include 

using namespace std;

typedef long long LL;

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

int main()
{
    int n;
    scanf("%d", &n);
    while(n -- )
    {
        int a, p;
        scanf("%d%d", &a, &p);
        LL t = qmi(a, p - 2, p);
        if(t * a % p == 1) printf("%lld\n", t);
        else puts("impossible");
    }
    return 0;
}

三、扩展欧几里得算法

1.扩展欧几里得算法

裴蜀定理: a , b a,b a,b是整数,且 g c d ( a , b ) = d gcd(a,b)=d gcd(a,b)=d,那么对于任意的整数 x , y x,y x,y, a x + b y ax+by ax+by都一定是 d d d的倍数,特别地,一定存在整数 x , y x,y x,y,使 a x + b y = d ax+by=d ax+by=d成立。

a x + b y = d ax+by=d ax+by=d,对于下一状态: e x g c d ( b , a % b , y , x ) exgcd(b,a \%b,y,x) exgcd(b,a%b,y,x),有方程: b y + ( a − ⌊ a b ⌋ × b ) x = d by+(a- \lfloor \frac{a}{b} \rfloor \times b)x=d by+(aba×b)x=d,整理一下: a x + b ( y − ⌊ a b ⌋ × x ) = d ax+b(y-\lfloor \frac{a}{b} \rfloor \times x)=d ax+b(yba×x)=d
AcWing 877. 扩展欧几里得算法

#include 

using namespace std;

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()
{
    int n;
    scanf("%d", &n);
    while(n -- )
    {
        int a, b, x, y;
        scanf("%d%d", &a, &b);
        exgcd(a, b, x, y);
        printf("%d %d\n", x, y);
    }
    
    return 0;
}

2.线性同余方程

求出一个 x i x_i xi使得 a i × x i ≡ b i (   m o d   m i ) a_{i} \times x_{i} \equiv b_{i}\left(\bmod m_{i}\right) ai×xibi(modmi)成立。
对于 a × x ≡ b (   m o d   m ) a \times x\equiv b\left(\bmod m\right) a×xb(modm),一定有: a x = d m + b ⇒ a x − d m = b ax = dm+b \Rightarrow ax-dm=b ax=dm+baxdm=b,令 − d = y -d=y d=y
则有 a x + m y = b ax+my=b ax+my=b,最后做法如上所述!
AcWing 878. 线性同余方程

#include 

using namespace std;

typedef long long LL;

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

int main()
{
    int n;
    scanf("%d", &n);
    while(n -- )
    {
        LL a, b, m;
        scanf("%lld%lld%lld", &a, &b, &m);
        LL x, y;
        LL d = exgcd(a, m, x, y);
        if(b % d) puts("impossible");
        else printf("%lld\n", x * b / d % m);
    }
    
    return 0;
}

四、中国剩余定理

1.表达整数的奇怪方式

中国剩余定理: 假设整数 m 1 , m 2 , . . . , m n m_1,m_2, ... ,m_n m1,m2,...,mn两两互质,则对任意的整数: a 1 , a 2 , . . . , a n a_1,a_2, ... ,a_n a1,a2,...,an,方程组有解,并且通解可以用如下方式构造得到:
{ x ≡ a 1 (   m o d   m 1 ) x ≡ a 2 (   m o d   m 2 ) ⋮ x ≡ a n (   m o d   m n ) \left\{\begin{array}{c} x \equiv a_{1}\left(\bmod m_{1}\right) \\ x \equiv a_{2}\left(\bmod m_{2}\right) \\ \vdots \\ x \equiv a_{n}\left(\bmod m_{n}\right) \end{array}\right. xa1(modm1)xa2(modm2)xan(modmn)

我们可以两两进行构造, { x ≡ a 1 (   m o d   m 1 ) x ≡ a 2 (   m o d   m 2 ) ⇒ { x = k 1 m 1 + a 1 x = k 2 m 2 + a 2 \left\{\begin{array}{c}x \equiv a_{1}\left(\bmod m_{1}\right) \\ x \equiv a_{2}\left(\bmod m_{2}\right)\end{array}\right. \Rightarrow \left\{\begin{array}{c}x =k_1 m_{1}+a_1 \\ x =k_{2}m_2+a_2\end{array}\right. {xa1(modm1)xa2(modm2){x=k1m1+a1x=k2m2+a2
k 1 m 1 + a 1 = k 2 m 2 + a 2 ⇒ k 1 m 1 − k 2 m 2 = a 2 − a 1 k_1m_1+a_1=k_2m_2+a_2 \Rightarrow k_1m_1-k_2m_2=a_2-a_1 k1m1+a1=k2m2+a2k1m1k2m2=a2a1
该方程有解的条件是 ( m 1 , m 2 ) ∣ a 2 − a 1 (m_1,m_2)|a_2-a_1 (m1,m2)a2a1


二元一次不定方程的通解形式
若二元一次不定方程 a x + b y = n a x+b y=n ax+by=n 有解, x 0 , y 0 x_{0}, y_{0} x0,y0 为它的一组整数解,则通解为
{ x = x 0 + b ( a , b ) ⋅ t y = y 0 − a ( a , b ) ⋅ t t ∈ Z \left\{\begin{array}{l} x=x_{0}+\frac{b}{(a, b)} \cdot t \\ y=y_{0}-\frac{a}{(a, b)} \cdot t \end{array} t \in Z\right. {x=x0+(a,b)bty=y0(a,b)attZ
所以 k 1 m 1 − k 2 m 2 = a 2 − a 1 k_1m_1-k_2m_2=a_2-a_1 k1m1k2m2=a2a1的通解有 { k 1 + m 2 d ⋅ K k 2 + m 1 d ⋅ K K ∈ Z \left\{\begin{array}{l} k_{1}+\frac{m_2}{d} \cdot K \\ k_{2}+\frac{m_1}{d} \cdot K \end{array} K \in Z \right. {k1+dm2Kk2+dm1KKZ
将其最小的一个通解带入 x = k 1 m 1 + a 1 x=k_1m_1+a_1 x=k1m1+a1,有 x = k 1 m 1 + m 2 d m 1 K + a 1 x=k_1m_1+\frac{m_2}{d}m_1K+a_1 x=k1m1+dm2m1K+a1
整理一下有: x = k 1 m 1 + a 1 + K m 1 m 2 d x=k_1m_1+a_1+K\frac{m_1m_2}{d} x=k1m1+a1+Kdm1m2, x = x 0 + k a x=x_0+ka x=x0+ka
AcWing 204. 表达整数的奇怪方式

#include 

using namespace std;

typedef long long LL;

LL exgcd(LL a, LL b, LL &x, LL &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()
{
    int n;
    scanf("%d", &n);

    LL m1, a1;
    bool flag = true;
    scanf("%lld%lld", &a1, &m1);
    for(int i = 0; i < n - 1; i ++ )
    {
        LL m2, a2;
        scanf("%lld%lld", &a2, &m2);
        LL k1, k2;
        LL d = exgcd(a1, a2, k1, k2);
        if((m2 - m1) % d)
        {
            flag = false;
            break;
        }
        LL t = a2 / d;
        k1 *= (m2 - m1) / d;

        k1 = (k1 % t + t) % t;

        m1 += a1 * k1;
        a1 = abs(a1 / d * a2);
    }

    if(flag) printf("%lld\n", (m1 % a1 + a1) % a1);
    else puts("-1");

    return 0;
}

你可能感兴趣的:(acwing学习总结,c++)