zoukankan      html  css  js  c++  java
  • 『笔记』数学数论(八)

    [Huge{(八)欧拉定理及其扩展} ]

    写在前面

    (2021.5.10 quad update) 添加了扩展欧拉定理、部分例题、欧拉群论的证明链接,优化代码模板。

    简介

    对于任意实数 (n) ,若满足 (gcd(a,n)=1),则有

    [a^{varphi(n)} equiv 1 pmod n ]

    欧拉函数

    定义

    欧拉函数 (varphi(n)) 表示在 (1 sim n) 中,于 (n) 互质的数的数量。

    已知上述 (a^{varphi(n)} equiv 1 pmod n),若等式两边同除于 (a) ,则有 (a^{varphi(n)-1} equiv cfrac{1}{a} pmod n).

    也就是说,在 (pmod n) 意义下,除以 (a) 就相当于乘以 (a^{varphi(n)-1}),而此时的 (varphi(n)) 被称为欧拉函数

    例如:(varphi(6) = 2)(varphi(30)=8)

    容斥原理求欧拉函数

    容斥原理

    [S=S_1+S_2+S_3-S_{1,2}-S_{1,3}-S_{2,3}+S_{1,2,3} ]

    应用

    已知一个整数 (n),它必然可以分解为 (p_1^{r_1}p_2^{r_2}p_3^{r_3}cdots p_k^{r_k}),其中 (p)(n) 的质因子。

    那么对于 (n),其中 (p_1) 及其倍数的因子数量即为 (cfrac{n}{p_1})(p_2) 及其倍数的因子数量即为 (cfrac{n}{p_2})(cdots)

    显然,(1 sim n) 中与 (n) 不互质的因子的数量即为 (cfrac{n}{p_1}+cfrac{n}{p_2}+cfrac{n}{p_3}+cdots+cfrac{n}{p_k})。相反,与 (n) 互质的因子的数量即 (varphi(n))(n - (cfrac{n}{p_1} + cfrac{n}{p_2} + cfrac{n}{p_3} + cdots + cfrac{n}{p_k}) + NUM - cfrac{n}{p_{1}p_{2}p_{3}cdots p_{k}})

    其中 (NUM) 为同时是多个因子的倍数的数的数量。

    不难得出,

    [egin{aligned} NUM= (cfrac{n}{p_{1}p_{2}} + cfrac{n}{p_{1}p_{3}} + cdots + cfrac{n}{p_{1}p_{k}})&+\ (cfrac{n}{p_{2}p_{1}} + cfrac{n}{p_{2}p_{3}} + cdots + cfrac{n}{p_{2}p_{k}})&+\ cdots &+\ (cfrac{n}{p_{k}p_{2}} + cfrac{n}{p_{k}p_{3}} + cdots + cfrac{n}{p_{k}p_{k-1}}) end{aligned} ]

    原式可以因式分解

    [egin{aligned} varphi(n) &=n - (cfrac{n}{p_1} + cfrac{n}{p_2} + cfrac{n}{p_3} + cdots + cfrac{n}{p_k}) + NUM - cfrac{n}{p_{1}p_{2}p_{3}cdots p_{k}}\ &=n(1-cfrac{1}{p_1})(1-cfrac{1}{p_2})(1-cfrac{1}{p_3})cdots (1-cfrac{1}{p_k}) end{aligned} ]

    代码

    int phi(int n) //求 varphi(n)
    {
        int res = n;
        for (int i = 2; i <= sqrt(n); i++)
            if (!(n % i))
            {
                res = res / i * (i - 1);
                while (!(n % i))
                    n = n / i;
            }
        if (n > 1)
            res = res / n * (n - 1);
        return res;
    }
    

    注意

    (gcd(a,n) eq 1) 时,(a)(pmod n) 意义下不存在逆元。

    也就是说,当 (a,n) 不满足 (a perp n) 时,(a,n) 无法在 (pmod n) 意义下作除法。

    欧拉函数表

    大多数情况下,欧拉函数是可以直接拿来用的,所以如果遇到此类题目,需要首先预处理欧拉函数表。

    const int _ = 1000010;
    int varphi[_], prime[_], num, fac[_];
    void Phi()
    {
        varphi[1] = 1;
        for (int i = 2; i <= n - 1; i++)
        {
            if (!fac[i])
            {
                prime[++num] = i;
                fac[i] = i;
                varphi[i] = i - 1;
            }
            for (int j = 1; j <= num; j++)
                if (prime[j] * i > n || prime[j] > fac[i])
                    break;
                else
                {
                    fac[prime[j] * i] = prime[j];
                    if (prime[j] < fac[i])
                        varphi[prime[j] * i] = prime[j] * varphi[i] - varphi[i];
                    else
                        varphi[prime[j] * i] = prime[j] * varphi[i];
                }
            varphi[i] += varphi[i - 1];
        }
    }
    

    附:逆元扩展

    已知一个质数 (p) ,一个实数 (n),且 (n<p) ,求 (1 sim n)(pmod p) 意义下的逆元。

    首先求出 (n!) 的逆元,则 (n) 的逆元为 ((n-1)!cdot (n!)^{-1}),即 (cfrac{(n-1)!}{n!}=cfrac{1}{n})

    依此类推,将 ((n!)^{-1}) 乘以 (n),可得到 ([(n-1)!]^{-1}) (cdots)

    直到推到 (1)

    时间复杂度 (O(n))

    YES!

    例题

    P2158 [SDOI2008]仪仗队

    对于此题,非常显然每个点与原点生成的矩形对角线上的所有点,都只能看到第一个。也就是说,对于任意整点 ((x,y)) ,点 ((lambda x,lambda y))((lambda >1)) 都是看不到的。

    那么有

    [gcd(lambda x,lambda y) = lambda gcd(x,y) >lambda cdot 1 > 1 ]

    即对于一个点 ((x,y)) ,若其满足 (gcd(x,y) eq 1) 时,这个点必定看不到。

    相反,若其满足 (gcd(x,y)=1) ,且它会被遮挡,则一定存在一个 (mu(mu >1)) 使得 ((cfrac{x}{mu},cfrac{y}{mu})) 能被看到。

    又因为 (gcd(x,y)=1),所以 (mu) 一定不存在。

    那么可以得出一个结论:一个点 ((x,y)) 能被看到,当且仅当 (gcd(x,y)=1)

    (n=1) 时,答案为 (0)

    否则,答案为 (sum limits_{x=0}^{n-1} sum limits_{y=0}^{n-1}[gcd(x, y)=1], n geq 2)

    展开

    [egin{aligned} sum limits_{x=0}^{n-1} sum limits_{y=0}^{n-1}[gcd(x, y)=1] = sum_{x=0}^{n-1} sum_{y=0}^{x-1}[gcd(x, y)=1]&+\ sum_{x=0}^{n-1} sum_{y=x}^{x}[gcd(x, y)=1] &+\ sum_{x=0}^{n-1} sum_{y=x+1}^{n-1}[gcd(x, y)=1]&, end{aligned} ]

    其中 (n geq 2)

    根据

    [sum limits_{x=0}^{n-1} sum limits_{y=0}^{x-1}[gcd(x, y)=1]=sum limits_{x=0}^{n-1} sum_{y=x+1}^{n-1}[gcd(x, y)=1] ]

    [ ext{原式}= 2 sum limits_{x=0}^{n-1} sum limits_{y=0}^{x-1}[g c d(x, y)=1]+1 ]

    其中 (n geq 2)

    引入欧拉函数 (varphi(n))

    [varphi(n)=sum limits_{i=0}^{n-1}[gcd(i, n)=1] ]

    [ ext{ans}(n)=2 sum limits_{x=0}^{n-1} varphi(x)+1 ]

    其中 (n geq 2)

    综上所述,

    [ans= left{ egin{array}{l} 0 & n=1 \ 2 sum limits_{x=0}^{n-1} varphi(x)+1 & n geq 2 end{array} ight. ]

    /*
    
    Name: P2158 [SDOI2008]仪仗队
    
    Solution: 欧拉函数
       
    
    By Frather_
    
    */
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    #include <queue>
    #define int long long
    #define InF 0x3f3f3f3f
    #define kMax 10e5
    #define kMin -10e5
    #define kMod 998244353
    using namespace std;
    /*==================================================快读*/
    inline int read()
    {
        int X = 0, F = 1;
        char CH = getchar();
        while (CH < '0' || CH > '9')
        {
            if (CH == '-')
                F = -1;
            CH = getchar();
        }
        while (CH >= '0' && CH <= '9')
        {
            X = (X << 3) + (X << 1) + (CH ^ 48);
            CH = getchar();
        }
        return X * F;
    }
    /*===============================================定义变量*/
    int n;
    
    const int _ = 1000010;
    int phi[_], prime[_], num, fac[_];
    /*=============================================自定义函数*/
    void Phi()
    {
        phi[1] = 1;
        for (int i = 2; i <= n - 1; i++)
        {
            if (!fac[i])
            {
                prime[++num] = i;
                fac[i] = i;
                phi[i] = i - 1;
            }
            for (int j = 1; j <= num; j++)
                if (prime[j] * i > n || prime[j] > fac[i])
                    break;
                else
                {
                    fac[prime[j] * i] = prime[j];
                    if (prime[j] < fac[i])
                        phi[prime[j] * i] = prime[j] * phi[i] - phi[i];
                    else
                        phi[prime[j] * i] = prime[j] * phi[i];
                }
            phi[i] += phi[i - 1];
        }
    }
    /*=================================================主函数*/
    signed main()
    {
        n = read();
        Phi();
        printf("%lld
    ", n == 1 ? 0 : 2 * phi[n - 1] + 1);
        return 0;
    }
    

    扩展欧拉定理

    我们已知,只有当 (a,n) 满足 (gcd(a,n)=1) 时,欧拉定理才成立,那么若 (a,b) 不互质呢?

    定义

    对于 (a,n)(gcd(a,n) eq 1) ,时,有

    [a^c equiv left{ egin{array}{ll} a^{c mod{varphi(n)} } & gcd(a,n) = 1 \ a^c & gcd(a,n) eq 1 ext{且} c < varphi(n) \ a^{c mod{varphi(n) + varphi(n)} } & gcd(a,n) eq 1 ext{且} c geq varphi(n) end{array} ight. ]

    证明

    不妨首先考虑一个质因子 (p),令 (n=s cdot p^r ,gcd(s,p)=1)

    (p^{varphi(s)} equiv 1 pmod s) , 又由 (gcd(s,p)=1) ,则有 (varphi(s) mid varphi(n)),即 (p^{varphi(n)} equiv 1 pmod s)

    根据同余式性质,两边同时乘以一个 (p^r)

    [p^{varphi(n)+r} equiv p^r pmod n ]

    那么有

    [p^c equiv p^{c-r+r} equiv p^{varphi(n) + c} pmod n ]

    其中 (c geq r)

    显然, (r leq varphi(n))

    因此

    [p^r equiv p^{varphi(n)+c} pmod n ]

    其中 (c geq varphi(n))

    上式可转化为

    [p^c equiv p^{c mod{varphi(n)} + varphi(n)} pmod n ]

    其中 (c geq varphi(n))

    类似的,可以有

    [egin{aligned} (p^k)^c &= p^{kc} \ &equiv p^{varphi(n) + kc} \ &equiv p^{k varphi(n) + kc} = (p^k)^{varphi(n) + c} \ &equiv (p^k)^{c mod varphi(n) + varphi(n)} end{aligned} pmod n ]

    其中 (c geq varphi(n),k>0)

    由于 (p)(a) 的因子,即 (a = prod p_{i}^{k_{i}}) ,而对于每一个 (p) 都满足上述证明,那么其乘积也必然满足,所以有

    [a^c equiv a^{c mod varphi(n) + varphi(n)} pmod n ]

    其中 (c geq varphi(n))

    证毕。

    例题

    P5091 【模板】扩展欧拉定理

    RT,模板题。

    /*
    
    Name: P5091 【模板】扩展欧拉定理
    
    Solution: 扩展欧拉定理
       
    
    By Frather_
    
    */
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    #include <queue>
    #define int long long
    #define InF 0x3f3f3f3f
    #define kMax 10e5
    #define kMin -10e5
    #define kMod 998244353
    using namespace std;
    /*==================================================快读*/
    inline int read()
    {
        int X = 0, F = 1;
        char CH = getchar();
        while (CH < '0' || CH > '9')
        {
            if (CH == '-')
                F = -1;
            CH = getchar();
        }
        while (CH >= '0' && CH <= '9')
        {
            X = (X << 3) + (X << 1) + (CH ^ 48);
            CH = getchar();
        }
        return X * F;
    }
    /*===============================================定义变量*/
    int a, m, b;
    /*=============================================自定义函数*/
    inline int read_(int kmod)
    {
        int X = 0;
        bool F = false;
        char CH = getchar();
        while (CH < '0' || CH > '9')
            CH = getchar();
        while (CH >= '0' && CH <= '9')
        {
            X = (X << 3) + (X << 1) + (CH ^ 48);
            if (X >= kmod)
                F = true;
            X %= kmod;
            CH = getchar();
        }
        return X + (F ? kmod : 0);
    }
    
    int phi(int n) //求 varphi(n)
    {
        int res = n;
        for (int i = 2; i <= sqrt(n); i++)
            if (!(n % i))
            {
                res = res / i * (i - 1);
                while (!(n % i))
                    n = n / i;
            }
        if (n > 1)
            res = res / n * (n - 1);
        return res;
    }
    
    int gcd(int a, int b)
    {
        return b ? gcd(b, a % b) : a;
    }
    
    int Qpow(int a, int t, int p)
    {
        int res = 1;
        while (t)
        {
            if (t & 1)
                res = res * a % p;
            a = a * a % p;
            t >>= 1;
        }
        return res;
    }
    /*=================================================主函数*/
    signed main()
    {
        a = read();
        m = read();
        int t = phi(m);
        b = read_(t);
        int g = gcd(a, m);
        // if (g = 1)
        //     printf("%lld
    ", Qpow(a, b % t, m));
        // else if (g != 1 && b < t)
            printf("%lld
    ", Qpow(a, b, m));
        // else if (g != 1 && b >= t)
        //     printf("%lld
    ", Qpow(a, b % t + t, m));
        return 0;
    }
    

    欧拉定理的群论证明

    详情可参见这篇博客

  • 相关阅读:
    LeetCode 623. Add One Row to Tree
    LeetCode 894. All Possible Full Binary Trees
    LeetCode 988. Smallest String Starting From Leaf
    LeetCode 979. Distribute Coins in Binary Tree
    LeetCode 814. Binary Tree Pruning
    LeetCode 951. Flip Equivalent Binary Trees
    LeetCode 426. Convert Binary Search Tree to Sorted Doubly Linked List
    LeetCode 889. Construct Binary Tree from Preorder and Postorder Traversal
    LeetCode 687. Longest Univalue Path
    LeetCode 428. Serialize and Deserialize N-ary Tree
  • 原文地址:https://www.cnblogs.com/Frather/p/14734191.html
Copyright © 2011-2022 走看看