• 洛谷 P1593 因子和 题解


    前导知识:

    1、算术基本定理(唯一分解定理)
    (a={p_1}^{k_1} * {p_2}^{k_2}... * {p_m}^{k_m})

    2、快速幂

    3、约数和公式
    (sigma(a)=prodlimits_{i=1}^{k}(1+p_{i}+p_{i}^{2}+cdots+p_{i}^{r_{i}}))

    4、逆元概念及求法

    5、矩阵快速幂
    TODO

    解法1【多项式展开+逆元】

    既然要求约数和,那先分解质因数。根据唯一分解定理:

    $a={p_1}^{k_1} * {p_2}^{k_2} * {p_3}^{k_3} * ... * {p_n}^{k_n}$

    因为本题要分解的是(a^b),上式写成:

    $a^b={p_1}^{k_1 * b} * {p_2}^{k_2 * b} * {p_3}^{k_3 * b} * ... * {p_n}^{k_n * b}$

    要求的是约数和,套用约数和公式

    $sigma(n)=prodlimits_{i=1}^{k}(1+p_{i}+p_{i}^{2}+cdots+p_{i}^{r_{i}})$

    依题意,本题:
    (ans=(1+p_1 + {p_1}^2 + {p_1}^3 +...+{p_1}^{k_1+b}) * (1+p_2 + {p_2}^2 + {p_2}^3 +...+{p_2}^{k_2+b}) *... * (1+p_n + {p_n}^2 + {p_n}^3 +...+{p_n}^{k_n+b}))

    根据在约数和公式那篇文章总结的经验,我们知道,是可以采用等比数列求和公式进行计算约数和的:

    $1+p_1 + {p_1}^2 + {p_1}^3 +...+{p_1}^{k_1+b}=frac{p_1^{k_1+b+1}-1}{p_1-1}$

    多个连乘积就是:

    $ans=prodlimits_{i=1}^{k} frac{p_i^{k_i+b+1}-1}{p_i-1}$

    这个又是(k_i+b+1)次方,又是连乘积,结果很快就会非常大,计算机会装不下的,还好题目告诉可以取模,(MOD=9901)看清楚啊,这个(9901)是个质数啊!这非常重要啊!它要不是质数,不能用后面的费马小定理啊,那就要用扩展欧几里德了

    现在整理一下思路:

    (1)那个大乘方怎么取模呢?我们不能真的暴力算出(p_i^{k_i+b+1})啊,其实我们想算的是(p_i^{k_i+b+1} mod 9901)啊!
    答:快速幂又能快速算出(N)次方,又能在算的过程中取模,(Match!)

    (2)分数(frac{p_i^{k_i+b+1}-1}{p_i-1})怎么取模呢?
    答: 这样分两种情况分别讨论:

    2.1 分母(p_i-1)不是(MOD)的倍数,即(p_i-1)(MOD)互质:
    如果分数求模要使用逆元。通过逆元将除(p-1)转化为乘(p−1)的逆元即可。

    2.2 分母(p_i-1)(MOD)的倍数
    这时逆元是不存在的,也不要求啦!求也是没有~~哪咋办啊,5555~

    这么复杂??一个个来吧,

    办法如下:

    先看存在逆元的,就是((p-1)\%MOD!=0)

    在前导知识中讲过,求逆元有两种办法,分别适用于

    (MOD)是质数,并且(p-1)不是(MOD)的整数倍--- >费马小定理+快速幂 本题可以使用这个!
    (p)是质数的话,并且(b)不是(p)的整数倍,那么(b^{p−2})就是(b)在模(p)下的逆元。

    (MOD) 可以是质数,也可以不是质数,但(p-1)不是(MOD)的整数倍--->扩展欧几理德算法 [这个方法似乎更万能啊,但一般MOD都是质数,所以费马小定理就应该OK]

    如果不满足 "(p-1)不是(MOD)的整数倍" 这个条件,就不存在逆元

    那逆元不存在怎么办,就是((p-1)\%MOD=0) 也就是 (p-1)(MOD)的整数倍时:

    不用担心,因为对应上面的式子(p-1),有:

    $(p−1) mod 9901=0$
    即:
    $p mod 9901=1$

    所以(ans=1+(p mod 9001)^1 + (p mod 9001)^2 +(p mod 9001)^3+...+(p mod 9001)^n)

    即:

    $ans=1+n$

    至此,本题就分析完了。

    #include<bits/stdc++.h>
    
    using namespace std;
    
    typedef long long LL;
    const int N = 10010;  //质数因子个数上限,因为a的数据范围是5*10^7,开根号就是10^4肯定能装得下了
    const int MOD = 9901; //要模的质数
    int primes[N];        //质数因子数组
    int idx;              //质数因子数组下标游标
    LL res = 1;           //结果,因为是要乘来乘去,初始值需要给1
    int a, b;             //a的b次方
    unordered_map<int, int> mc; //mc对应着幂次 key:质数 value:个数
    
    // 快速幂模板[与yxc的有一点点区别,把p=MOD写在代码里了,减少了参数传入]
    int qmi(int a, int k) {
        int res = 1;                              //答案
        while (k) {                               //一路让k变小直到为0停止
            if (k & 1) res = (LL) res * a % MOD;  //如果k的个位是1的话
            k >>= 1;                              //右移一位
            a = (LL) a * a % MOD;                 //1-2-4-8-16,就是每进一位,是把a=a*a,注意使用long long 防止在乘积过程中爆了int
        }
        return res;
    }
    
    //约数和公式的一部分,等比数列求和公式
    // res=(p^(k+1)-1)/(p-1)
    // 这里k还要乘一个b
    int solve(int p, int k) {
        int res;
        k *= b;                                       //每一个质因子的幂次都还要乘以原题中的指数的大小
        if ((p - 1) % MOD == 0) res = (k + 1) % MOD;  //当a,p不互质时,逆元不存在 (p-1)%MOD=0,则p%MOD=1,
            // 此时,就可以直接计算原始公式 res=1+(p mod 9901)^1+(p mod 9901)^2+(p mod 9901)^3+...+(p mod 9901)^k
            // 而不是面对等比数列求和公式那个带分母的式子,因为我们无法处理除法取模,这时逆元不存在!
            // res=k+1
        else {
            //MOD肯定是质数,而且 (p-1)%MOD!=0,符合费马小定理,在这里就是 (p-1)^(MOD-2)
            int inv = qmi((p - 1) % MOD, MOD - 2); //费马小定理求出逆元
            //利用等比数列求和公式
            res = (qmi(p % MOD, k + 1) - 1) % MOD * inv % MOD;
        }
        return res;
    }
    
    int main() {
        //读入a和b,准备计算a^b
        scanf("%d%d", &a, &b);
    
        /***下面的代码在分解质数因子*************************************************************/
        //a分解质因子,求其幂次
        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;
        }
        /***上面的代码在分解质数因子*************************************************************/
        //连乘,遍历所有质数因子
        for (int i = 1; i <= idx; i++) res = res * solve(primes[i], mc[i]) % MOD;
    
        //输出
        printf("%d
    ", (res % MOD + MOD) % MOD);//怀疑测试数据14中有负数,所以这样写才能通过!
        return 0;
    }
    
  • 相关阅读:
    经典测试面试题一
    自定义函数实现字符串数组互转
    php 实现密码错误三次锁定账号10分钟
    HTTP状态码
    Linux知识点
    慢查日志
    curl 采集
    HTTP协议
    docker配置
    mysql数据库索引、存储引擎、事务
  • 原文地址:https://www.cnblogs.com/littlehb/p/14948934.html
Copyright © 2020-2023  润新知