算法刷题【洛谷P1593】因子和(附等比数列求和公式推导)

异想之旅:本人原创博客完全手敲,绝对非搬运,全网不可能有重复;本人无团队,仅为技术爱好者进行分享,所有内容不牵扯广告。本人所有文章仅在CSDN、掘金和个人博客(一定是异想之旅域名)发布,除此之外全部是盗文!


洛谷 P1593 因子和

题目描述

输入两个整数 a a a b b b,求 a b a^b ab 的因子和。

由于结果太大,只要输出它对 9901 9901 9901 取模的结果。

输入格式

仅一行,为两个整数 a a a b b b

输出格式

输出一行一个整数表示答案对 9901 9901 9901 取模的结果。

输入输出样例

In 1:

2 3

Out 1:

15
数据范围

对于全部的测试点,保证 1 ≤ a ≤ 5 × 1 0 7 1 \leq a \leq 5 \times 10^7 1a5×107 0 ≤ b ≤ 5 × 1 0 7 0 \leq b \leq 5 \times 10^7 0b5×107

题解

本文视频讲解:

【洛谷P1593】因子和视频讲解

为了完成洛谷P1593这道题也是拼了……用到了三个不会的知识:因子和公式等比数列求和公式费马小定理求分数

这里另摆一份很好的题解:洛谷 [P1593 因子和] {快速幂+费马小定理求逆元+求解质因子} 奋斗的珂珂~ - 代码先锋网


先摆出因子和公式的参考资料:


等比数列定义很简单,设有数列 [ a 1 , a 2 , . . . , a n ] [a_1, a_2,...,a_n] [a1,a2,...,an] ,存在一个定值 q q q 使得任意 1 ≤ i ≤ n − 1 1 ≤ i ≤ n-1 1in1 都有 a i + 1 = a i × q a_{i+1} = a_i \times q ai+1=ai×q 。由此定义可知 a n = a 1 × q n − 1 a_n = a_1 \times q^{n-1} an=a1×qn1

这个数列的求和公式为 S n = a 1 + a 2 + . . . + a n = a 1 q n − 1 q − 1 S_n = a_1 + a_2 + ... + a_n = a_1 \frac{q^n-1}{q-1} Sn=a1+a2+...+an=a1q1qn1 ,下面我们来证明这个公式:

∵ q S n = q a 1 + q a 2 + . . . + q a n = a 2 + a 3 + . . . + a n + 1 ∵ qS_n = qa_1 + qa_2 + ... + qa_n = a_2 + a_3 + ... + a_{n+1} qSn=qa1+qa2+...+qan=a2+a3+...+an+1

∴ q S n − S n = ( q − 1 ) S n = a n + 1 − a 1 = ( a 1 ∗ q n ) − a 1 = a 1 ( q n − 1 ) ∴ qS_n - S_n= (q-1) S_n = a_{n+1} - a_1 = (a_1 * q^n) - a_1 = a_1(q^n-1) qSnSn=(q1)Sn=an+1a1=(a1qn)a1=a1(qn1)

∴ S n = a 1 q n − 1 q − 1 ∴ S_n = a_1 \frac{q^n-1}{q-1} Sn=a1q1qn1

(参考资料:等比数列公式及推导_高三网


费马小定理求分数快速幂的公式及证明:

在这里插入图片描述

(来源:分数取模(费马小定理)_give it a try-CSDN博客_分数取模运算


根据整数的唯一分解定理,整数a进行质因数分解对应的式子唯一,有:

a = p 1 k 1 ∗ p 2 k 2 ∗ p 3 k 3 ∗ … ∗ p n k n a = p_1^{k_1} * p_2^{k_2} *p_3^{k_3}* … * p_n^{k_n} a=p1k1p2k2p3k3pnkn

又因为本题要分解的是 a b a^b ab,所以上面的式子又可以写成这样:

a b = p 1 k 1 ∗ b ∗ p 2 k 2 ∗ b ∗ p 3 k 3 ∗ b ∗ … ∗ p n k n ∗ b a^b= p_1^{k_1*b} * p_2^{k_2*b} *p_3^{k_3*b}* … * p_n^{k_n*b} ab=p1k1bp2k2bp3k3bpnknb

证明很简单,就是把上面第一个式子乘上 b b b 次即可得第二个式子

接下来我们要求的是因子和,所以就有:

a n s = ( 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 1 k 2 ∗ b ) ∗ . . . ∗ ( 1 + p n 1 + p n 2 + p n 3 + … + p n k n ∗ b ) 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_1^{k_2*b})*...*(1+p_n^1 + p_n^2 +p_n^3+ … + p_n^{k_n*b}) ans=(1+p11+p12+p13++p1k1b)(1+p21+p22+p23++p1k2b)...(1+pn1+pn2+pn3++pnknb)

于是求和转化成了等比数列的和的乘积

而对于每一个等比数列,可根据公式求和: s u m = p i k i ∗ b + 1 − 1 p i − 1 sum=\frac{p_i^{k_i*b+1}-1}{p_i-1} sum=pi1pikib+11 (等比数列求和公式中代入 a 1 = 1 a_1 = 1 a1=1

注意这里的 p p p 与上面截图中的 p p p 含义不一样!这里指的是公比(类比等差数列的公差),上面指的是 mod (快速幂中取余的那个数)

有了这些知识来看代码:

#include <bits/stdc++.h>
using namespace std;
const int mod = 9901;
int ys[50000000];

// 一个不那么简单的快速幂模板
long long ksm(long long x, long long y) {
    if (y == 0) return 1;
    if (y % 2 == 0) return ((long long)pow(ksm(x, y / 2) % mod, 2) % mod);
    return (x * (long long)pow(ksm(x, y / 2) % mod, 2) % mod);
}

int main() {
    int a, b;
    cin >> a >> b;
    int aa = a;  // 由于aa的值会被修改,所以需要一个变量保存

    ys[2] = 0;
    while (a % 2 == 0) {
        a /= 2;
        ys[2]++;
    }
    ys[2] *= b;  // 因为是b次幂因此要乘以b
    for (int i = 3; i <= a / i; i += 2) {
        ys[i] = 0;
        while (a % i == 0) {
            a /= i;
            ys[i]++;
        }
        ys[i] *= b;
    }
    if(a != 1) {
        // 分解质因数,若有质因数超过根号a,则只能是a本身
        ys[a] = b;
    }

    long long ans = 1;
    for (int i = 2; i <= aa; i++) {
        if (!ys[i]) continue;  // 不存在该质因数
        int temp;
        if (i % mod == 1) {
            temp = (ys[i] + 1) % mod;  // 详见博文最后的解释
        } else
            temp = ((ksm(i, ys[i] + 1) - 1 + mod) % mod) * ksm(i - 1, mod - 2) % mod;  // 使用费马小定理求解
        if (temp != 0) ans = ans * temp % mod;
    }
    cout << ans << endl;

    return 0;
}

关于代码第42行特判 i % mod == 1 (也就是判断 i-1 是否为 mod 的倍数;同样适用于45行 temp != 0 特判):

i-1mod 的倍数时,等比数列中每一项取余 9901 9901 9901 的结果都为 1 1 1 ,等比数列的和便是 k i × b (上方证明公式中的表示) = y s [ i ] (代码中的表示) k_i \times b(上方证明公式中的表示) = ys[i](代码中的表示) ki×b(上方证明公式中的表示)=ys[i](代码中的表示) ,因此此处特判改为 temp = ys[i] + 1 ;同理,若 imod 的倍数,则 temp 值为 0 0 0 恒成立(洛谷测试点中不存在此极端情况因此不考虑也能AC(又更新:目前洛谷已加强))

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

异想之旅

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值