• 质因数分解专题


    一、单个数质因数分解

    直接上代码:

    #include <bits/stdc++.h>
    
    using namespace std;
    
    /**
     * 功能:分解质数因数
     * @param a 待分解的数字
     */
    const int N = 1010;
    int primes[N];              //质数因子数组
    int idx;                    //质数因子数组下标游标
    int mc[N];                  //mc对应着幂次
    void Decomposition(int a) {
        //清空,防止重复
        memset(primes, 0, sizeof primes);
        memset(mc, 0, sizeof mc);
        idx = 0;
        //开始
        for (int i = 2; i * i <= a; i++) {
            //如果发现a的一个因子i
            if (a % i == 0) {
                primes[++idx] = i;      //质因子数组维护
                mc[idx] = 1;            //指数幂次数+1
                a /= i;                 //去掉这个因子
                while (a % i == 0) {    //除干净它
                    mc[idx]++;          //指数幂次数+1
                    a /= i;             //继续缩小
                }
            }
        }
        //可能剩余一个很大的质因子数
        if (a > 1) {
            primes[++idx] = a;
            mc[idx] = 1;
        }
    }
    
    int main() {
        //求2~100区间内所有数字的质因子
        for (int i = 2; i <= 100; i++) {
            cout << i << ":     ";
            //分解质因子
            Decomposition(i);
            //遍历输出质因子
            for (int j = 1; j <= idx; j++) {         //遍历每个质因子
                for (int k = 1; k <= mc[j]; k++) {  //遍历质因子的个数
                    cout << primes[j];
                    //最后一个因子,并且是最后一个因子的最后一个输出,后面不加*
                    if (j == idx && k == mc[j]) continue;
                    cout << "*";
                }
            }
            cout << endl;
        }
        return 0;
    }
    
    

    适用场景:单个数字的质因数分解。如果是区间内质因数分解,这个办法就太慢了。

    二、区间内质因数分解

    1、朴素版本

    思路:

    1、先 get_primes(sqrt(r))取得所有\(r\)的所有可能小质数因子(采用欧拉筛,即线筛)。

    2、双重循环:外层循环遍历\([l,r]\)之间的每个数字,内层循环遍历筛出来的每一个质数因子,如果能被整除掉,就一直除,直到不再包含这个质数因子。

    3、除完已知小质数列表后,\(i\)有两种可能 ,第一种\(i=1\),表示被所有小质数因数除干净了;另一种是\(i>1\),表示还存在一个大于\(sqrt(x)\)的大质数因数。将找到的所有可能质数因子记录到一个vector<int> v[N]中,

    附:C++实现质因子分解的理论基础

    优点:
    因为采用了先穷举根号内质数因数的办法,所以去掉了很多无用的尝试,对比传统暴力的分解质因数方法

    #include <bits/stdc++.h>
    
    using namespace std;
    const int INF = 0x3f3f3f3f;
    
    typedef long long LL;
    LL l = 1, r = 2000000;
    
    const int N = 1e7 + 10;
    //欧拉筛[线性筛法]
    int primes[N], cnt;     // primes[]存储所有素数
    bool st[N];             // st[x]存储x是否被筛掉
    void get_primes(int n) {
        for (int i = 2; i <= n; i++) {
            if (!st[i]) primes[cnt++] = i;
            for (int j = 0; primes[j] <= n / i; j++) {
                st[primes[j] * i] = true;
                if (i % primes[j] == 0) break;
            }
        }
    }
    
    vector<int> v[N];
    
    int main() {
        /**=====================================================***/
        //计时开始
        clock_t startTime= clock();
        /**=====================================================***/
    
        //1、筛出小于sqrt(r)的所有质数
        get_primes(sqrt(r));
    
        //2、遍历每个数字,求质因子
        for (LL i = l; i <= r; i++) {
            int t = i;
            for (int j = 0; j < cnt && primes[j] * primes[j] <= i; j++) {
                if (i % primes[j] == 0) {
                    v[i].push_back(primes[j]);
                    //除干净这个质数因子
                    while (t % primes[j] == 0) t /= primes[j];
                }
            }
            if (t > 1) v[i].push_back(t);
        }
    
        //输出质数因子有哪些
        for (int i = l; i <= r; i++) {
            cout << i << ":       ";
            for (auto c:v[i]) cout << c << " ";
            cout << endl;
        }
        cout << endl;
    
        /**************************************************************/
        //计时结束
        clock_t endTime = clock();
        cout << "The run time is: " <<(double)(endTime - startTime) / CLOCKS_PER_SEC << "s" << endl;
        /**=====================================================***/
        //The run time is: 4.273s
        return 0;
    }
    

    2、优化版本

    前导知识:整数除法向上取整

    在前面基础上进行优化。
    原理:上面是双重循环,遍历每一个数字求质数因子。 这里是遍历所有质数因子,成倍的筛出质数的倍数,是单层循环,速度当然更快,类似于埃筛的思路。

    #include <bits/stdc++.h>
    
    using namespace std;
    
    typedef long long LL;
    LL l = 1, r = 2000000;
    
    const int N = 1e7 + 10;
    //欧拉筛[线性筛法]
    int primes[N], cnt;     // primes[]存储所有素数
    bool st[N];             // st[x]存储x是否被筛掉
    void get_primes(int n) {
        for (int i = 2; i <= n; i++) {
            if (!st[i]) primes[cnt++] = i;
            for (int j = 0; primes[j] <= n / i; j++) {
                st[primes[j] * i] = true;
                if (i % primes[j] == 0) break;
            }
        }
    }
    
    vector<int> v[N];
    
    int main() {
        /**=====================================================***/
        //计时开始
        clock_t startTime = clock();
        /**=====================================================***/
        //1、筛出小于sqrt(r)的所有质数
        get_primes(sqrt(r));
    
        //遍历每个小质数因子
        for (int i = 0; i < cnt; i++) {
            int p = primes[i];
            //1、利用数组下标的位移,记录数据
            //2、找到大于l的第一个p的倍数,然后,每次增加p,相当于找出p的整数倍
            for (LL j = ((l - 1) / p + 1) * p; j <= r; j += p)
                //在每个倍数中记录下这个共同的质数因子p,比如 6里面包含2,8里面包含2,10里面包含2,相当于埃筛一次找到所有p的整数倍
                v[j - l].push_back(p); //标识数组vec[j-l],也就是真实的l这个数字中,存在p这个因子。
        }
    
        //如果还存在大的质数因子
        for (LL i = l; i <= r; i++) {
            LL tmp = i; //将i拷贝出来给了tmp,tmp要不断的减少啦,而i要保留。
    
            //遍历每个已经找到的因子
            for (int p: v[i - l]) {
                //除干净
                while ((tmp % p) == 0) tmp /= p;
            }
            //如果还存在大的质数因子
            if (tmp > 1)v[i - l].push_back(tmp);
        }
    
        //输出质数因子有哪些
        for (int i = l; i <= r; i++) {
            cout << i << ":       ";
            for (auto c:v[i - l]) cout << c << " ";
            cout << endl;
        }
        cout << endl;
    
        /**************************************************************/
        //计时结束
        clock_t endTime = clock();
        cout << "The run time is: " << (double) (endTime - startTime) / CLOCKS_PER_SEC << "s" << endl;
        /**=====================================================***/
        //第一种方法用时:
        //The run time is: 4.273s
    
        //第二种方法用时:
        //The run time is: 1.677s
        return 0;
    }
    

    实测性能提高一倍以上。

  • 相关阅读:
    bzoj3757 苹果树
    bzoj2743 [HEOI2012]采花
    bzoj4241 历史研究
    bzoj4448 [Scoi2015]情报传递
    bzoj3295 [Cqoi2011]动态逆序对
    bzoj4034 [HAOI2015]T2
    bzoj3339 Rmq Problem
    BZOJ 1017 魔兽地图
    BZOJ 1021 循环的债务
    SUOI #37 清点更多船只
  • 原文地址:https://www.cnblogs.com/littlehb/p/15213111.html
Copyright © 2020-2023  润新知