zoukankan      html  css  js  c++  java
  • 互质与欧拉函数

    互质与欧拉函数

    1. 算法分析

    基本概念
    欧拉函数:1~N中与N互质的数的个数
    在算术基本定理中:N = (p1^a1^) * (p2^a2^) * ... *(pm^am^)
    一个数的欧拉函数: φ(N)=N * (1-1/p1) * (1-1/p2) * ... * (1-1/pm)
    且φ(1) = φ(2) = 1

    重要结论
    1~N中,(x, y) = 1的对数为: 前1~N的欧拉函数的前缀和 * 2 - 1

    常用思路
    很多求gcd(x, y)=p的问题,最后都需要转化为(x/p, y/p)=1,然后变为求互质的数目,转化为欧拉函数求解

    2. 板子

    1. 求N的欧拉函数 O(sqrt(N))
    int get_euler(int a)
    {
        long long res = a;  // 存储答案
        for (int i = 2; i <= a / i ;++i)  // 求出小于等于sqrt(a)的质数
        {
            if (a % i == 0)
            {
                res = res * (i - 1) / i;  // 欧拉函数公式求答案
                while (a % i == 0) a /= i;
            }
        }
        if (a > 1) res = res * (a - 1) / a;
        return res;
    }
    
    1. 求1~N的欧拉函数和 O(N)
    phi[1] = 1;
    long long res = 0;
    for (int i = 2; i <= n; ++i )  // 求质数
    {
        if (!st[i])   // 没记录就标记这个质数
        {
            prime[cnt++] = i;
            phi[i] = i - 1;  // 质数的欧拉函数为本身减一
        }
        for (int j = 0; prime[j] <= n / i; ++j)  // 枚举所有的质数
        {
            st[prime[j] * i] = 1;
            if (i % prime[j] == 0 ) // 如果i能够整除pj
            {
                phi[i * prime[j]] = prime[j] * phi[i];  // phi(i * pj) = pj * phi[i]
                break;
            }
            phi[i * prime[j]] = phi[i] * (prime[j] - 1);  // phis(i * pj) = phi(i) * (pj - 1)
        }
    }
    for (int i = 1; i <= n; ++i) res += phi[i];  // 累加每个数的欧拉函数
    

    3. 例题

    acwing201 可见的点
    在一个平面直角坐标系的第一象限内,如果一个点(x,y)与原点(0,0)的连线中没有通过其他任何点,则称该点在原点处是可见的。
    1000组测试样例, 每组测试样例1000个数
    Screenshot_20200529_201747_com.huawei.notepad.jpg

    /*
    如果(x, y)是互质的,那么说明x和y是最小的单位,不可以往后投影,也就是不能挡住其他点
    所以只需要判断当前的x上,有多少y和这个x互质
    那么欧拉函数
    求前n个点就是求前1~N的欧拉函数和
    */
    #include <bits/stdc++.h>
    
    using namespace std;
    
    typedef long long LL;
    int const N = 1e3 + 10;
    int phi[N], prime[N], cnt, st[N], n, c;
    
    int main() {
        cin >> c;
        for (int k = 1; k <= c; ++k) {
            memset(st, 0, sizeof st);
            memset(phi, 0, sizeof phi);
            memset(prime, 0, sizeof prime);
            cnt = 0;
            cin >> n;
            phi[1] = 1;
            LL res = 0;
            for (int i = 2; i <= n; ++i )  // 求质数
            {
                if (!st[i])   // 没记录就标记这个质数
                {
                    prime[cnt++] = i;
                    phi[i] = i - 1;  // 质数的欧拉函数为本身减一
                }
                for (int j = 0; prime[j] <= n / i; ++j)  // 枚举所有的质数
                {
                    st[prime[j] * i] = 1;
                    if (i % prime[j] == 0 ) // 如果i能够整除pj
                    {
                        phi[i * prime[j]] = prime[j] * phi[i];  // phi(i * pj) = pj * phi[i]
                        break;
                    }
                    phi[i * prime[j]] = phi[i] * (prime[j] - 1);  // phis(i * pj) = phi(i) * (pj - 1)
                }
            }
            for (int i = 1; i <= n; ++i) res += phi[i];  // 累加每个数的欧拉函数
            cout << k << " " << n << " " << res * 2 + 1 << endl;
        }
    }
    

    acwing220最大公约数
    给定整数N,求1<=x,y<=N且GCD(x,y)为素数的数对(x,y)有多少对。
    N~1e7
    Screenshot_20200529_202410_com.huawei.notepad.jpg

    /*
    本题求1~N中的(x, y),使得(x, y)=p(p为质数)
    可以把p除以到x和y中,得到(x/p,y/p)=1
    那么就是求[1, N/p]中,找出(t1, t2)=1
    通过画图可以转化为acwing201可见的点那题
    但本题和那题不一样的区别在于那题是求[0,N]内(x, y)的对数,答案为1~N的欧拉函数的前缀和*2+1
    而本题通过画图可以知道,是求2~N的欧拉函数的前缀和*2 + 1
    那么只要把phi[1]=0,然后求1~N的欧拉函数的前缀和即可
    */
    #include <bits/stdc++.h>
    
    using namespace std;
    
    typedef long long LL;
    int const N = 1e7 + 10;
    int prime[N], cnt, st[N], n, phi[N];
    LL sum[N];
    
    // 求1~N的欧拉函数的前缀和
    void init(int n) {
        phi[1] = 1;
        for (int i = 2; i <= n; ++i )  // 求质数
        {
            if (!st[i])   // 没记录就标记这个质数
            {
                prime[cnt++] = i;
                phi[i] = i - 1;  // 质数的欧拉函数为本身减一
            }
            for (int j = 0; prime[j] <= n / i; ++j)  // 枚举所有的质数
            {
                st[prime[j] * i] = 1;
                if (i % prime[j] == 0 ) // 如果i能够整除pj
                {
                    phi[i * prime[j]] = prime[j] * phi[i];  // phi(i * pj) = pj * phi[i]
                    break;
                }
                phi[i * prime[j]] = phi[i] * (prime[j] - 1);  // phis(i * pj) = phi(i) * (pj - 1)
            }
        }
        phi[1] = 0;
        for (int i = 1; i <= n; ++i) sum[i] = sum[i - 1] + 0ll + phi[i];
    }
    
    int main() {
        cin >> n;
        init(n);
        LL res = 0;
        for (int i = 0; i < cnt; ++i) {
            LL tmp = 1;
            int p = prime[i];
            tmp += (LL)sum[n / p] * 2;
            res += tmp;
        }
        cout << res;
        return 0;
    }
    

    acwing221龙哥的问题
    求出∑1≤i≤Ngcd(i,N)的值。
    N~1e9
    Screenshot_20200529_211754_com.huawei.notepad.jpg

    #include <bits/stdc++.h>
    
    using namespace std;
    
    int const N = 1e5 + 10;
    
    typedef long long LL;
    int n;
    
    LL get_euler(LL a)
    {
        LL res = a;  // 存储答案
        for (int i = 2; i <= a / i ;++i)  // 求出小于等于sqrt(a)的质数
        {
            if (a % i == 0)
            {
                res = res * (i - 1) / i;  // 欧拉函数公式求答案
                while (a % i == 0) a /= i;
            }
        }
        if (a > 1) res = res * (a - 1) / a;
        return res;
    }
    
    int main() {
        cin >> n;
        LL res = 0;
        for (int i = 1; i <= n / i; ++i) {
            if (n % i == 0) {
                res += i * get_euler(n / i);
                if (n/i != i) res += (n / i) * get_euler(i);    
            }
        }
        
        cout << res;
        return 0;
    }
    
  • 相关阅读:
    【JavaScript】照片库
    【JavaScript】事件介绍
    【JavaScript】函数返回值
    【JavaScript】打造自己的函数
    【JavaScript】函数—可重用的代码块
    【JavaScript】循环语句
    【JavaScript】笑话生成器
    【JavaScript】数组
    c#之线程通信
    c#之线程同步
  • 原文地址:https://www.cnblogs.com/spciay/p/13060323.html
Copyright © 2011-2022 走看看