洛谷 P1593 因子和 题解

一、前导知识

(1) 算术基本定理

1、唯一分解定理

2、约数和公式

(2) 相关数学知识扩展

3、等比数列求和公式

(3)取模办法

4、快速幂

5、费马小定理求逆元

二、解题思路

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)=\prod\limits_{i=1}^{k}(1+p_{i}+p_{i}^{2}+\cdots+p_{i}^{r_{i}})$

\(ans=(1+p_1^1 + {p_1}^2 + {p_1}^3 +...+{p_1}^{k_1*b}) * (1+p_2^1 + {p_2}^2 + {p_2}^3 +...+{p_2}^{k_2*b}) *... * (1+p_n^1 + {p_n}^2 + {p_n}^3 +...+{p_n}^{k_n*b})\)

2、计算等比数列

以第一个来举例讲解:\(sum_1=1+p_1^1 + {p_1}^2 + {p_1}^3 +...+{p_1}^{k_1*b}\)

利用等比数列求和公式

\(\large S_n=\frac{a_1*(q^n-1)}{q-1}\)

\(\large \because a_1=1,q=p_1,n=k_1*b+1\)

\(\large \therefore sum_1=\frac{(p_1^{k_1*b+1}-1)}{p_1-1}\)

这又是\(k_1*b+1\)次方,后面还需要连乘积,结果很快就会非常大,计算机会装不下的,还好题目告诉可以取模,\(mod=9901\)

3、思考如何取模

(1)不能真的暴力算出\(p_1^{k_1*b+1}\)吧?其实我们想算的是\(p_1^{k_1*b+1} \ mod \ 9901\)啊!

快速幂能快速算出\(n\)次方,又能在算的过程中取模,完美,此里使用快速幂

(2)\(\frac{p_i^{k_i*b+1}-1}{p_i-1}\)这个东东怎么取模呢?
首先根据性质:

\((a - b) \% p = (a\%p - b\%p) \%p\)

我们知道,减\(1\)不妨碍我们取模,问题的关键在于分数取模! 分数取模需要求逆元

4、对分数取模

分数取模有两种情况,一:存在逆元,二:不存在逆元:

如果\(b\)\(m\)互质,则\(\frac{a}{b}=a*x(mod \ m)\) ,即存在逆元;不互质就不存在逆元,也就不用求逆元,想其它办法。



  • 如果分母\(p-1\)\(9901\)互质:

求逆元,然后通过逆元将除\(p-1\)转化为乘\(p−1\)的逆元。

求逆元有两种办法:费马小定理和扩展欧几里得,费马小定理代码简单,但有限制:\(mod\)必须是质数。扩展欧几里得更通用,但代码长。

本题中\(mod=9901\)是质数,可以使用费马小定理:

$q^{mod−2}$是$q$在模$mod$下的逆元。

本题中 \(q=p-1\)

int inv = qmi((p - 1) % MOD, MOD - 2); //快速幂+费马小定理求出逆元


  • 如果分母\(p-1\)\(mod\)不互质
$(p−1)\ \%\ 9901=0$

也就是:

$p\ \%\ 9901=1$

\(sum=1+p^1 + {p}^2 + {p}^3 +...+{p}^{k_1*b}\)

根据性质:
\((a + b) \% p = (a\%p + b\%p) \%p\)

\(\therefore sum \% 9901=1+(p\ \% \ 9001)^1 + (p\ \% \ 9001)^2 +(p\ \% \ 9001)^3+...+(p\ \% \ 9001)^{k*b}\)

即:

$sum \% 9901 =1+k*b$


开始愉快的写代码吧:

#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; //要模的质数
LL res = 1;           //结果,因为是要乘来乘去,初始值需要给1
int a, b;             //a的b次方

// 快速幂模板[与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;
}

/**
 * 功能:约数和公式的一部分,等比数列求和公式
 * @param p
 * @param k
 * @return
 */
int sum(int p, int k) {
    int res;
    k *= b;                                       //滚动生成幂次
    if ((p - 1) % MOD == 0) res = (k + 1) % MOD;  //当p-1是MOD倍数时,逆元不存在 res=(k+1)%MOD 就是上面推导的含义,
    else {
        //MOD是质数
        int inv = qmi((p - 1) % MOD, MOD - 2); //快速幂+费马小定理求出逆元
        //利用等比数列求和公式
        res = (qmi(p % MOD, k + 1) - 1) % MOD * inv % MOD;
    }
    return res;
}

/**
 * 功能:分解质数因数
 * @param a 待分解的数字
 */
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() {
    //读入a和b,准备计算a^b
    cin >> a >> b;

    //对a分解质因子
    Decomposition(a);

    //连乘,遍历所有质数因子
    for (int i = 1; i <= idx; i++)
        res = res * sum(primes[i], mc[i]) % MOD;

    //输出
    printf("%d\n", (res % MOD + MOD) % MOD);//测试数据14中有负数,这样写才能通过!
    return 0;
}
posted @ 2021-06-29 10:50  糖豆爸爸  阅读(162)  评论(0编辑  收藏  举报
Live2D