约数之和

题意:假设现在有两个自然数 $A$和$B$,$S$ 是 $A^B$ 的所有约数之和。求$S$ mod $9901$的值。
 

前置知识

 

唯一分解定理可得:一个数可以表达成$ A = {p_{1}}^{k_{1}}\times {p_{2}}^{k_{2}}\times {p_{3}}^{k_{3}}\times...\times {p_{n}}^{k_{n}} $的形式。$p_i$为不同的质数。
 
$A$的每个约数都可以看作从上式的每一项中抽取了特定个数($0$~$k_i$个)的$p_i$,最后乘在一起得到的。
 
设某约数是从第一项中选了$cnt1$个,第二项中选了$cnt2$个,... ,第$n$项中选了$cntn$个得到的,那么该约数可以表达为:$p_1 ^ {cnt1} * p_2 ^ {cnt2}  ... * p_n ^ {cntn}$;
 
特别的当 $cnt1$ = $cnt2$ = ... = $cntn$ = 0时,约数表示为1。
  
 

两个重要的公式

 
  • 约数个数

$$ \left ( k_{1}+1 \right )\times \left ( k_{2}+1 \right )\times \left ( k_{3}+1 \right )\times ...\times\left ( k_{n}+1 \right ) $$

  对每一项$p_i$而言,可以的选择的个数为 $0$ ~ $k_i$个,一共$k_i$ + $1$种,所以总共有($k_1$ + $1$) * ($k_2$ + $1$) * ... * ($k_n$ + $1$)种选择,即约数的个数。

  只要选择稍有不同,得到的约数就不一样,不会重复,也不会遗漏。
 
 
  • 约数之和

$$ \left ( p_{1}^{0} + p_{1}^{1} + p_{1}^{2} +...+ p_{1}^{k - 1} + p_{1}^{k} \right ) \times \left ( p_{2}^{0} + p_{2}^{1} + p_{2}^{2} +...+ p_{2}^{k - 1} + p_{2}^{k} \right ) \times ...\times \left ( p_{n}^{0} + p_{n}^{1} + p_{n}^{2} +...+ p_{n}^{k - 1} + p_{n}^{k} \right ) $$
 
  为什么上面这个式子可以表示约数的和呢,因为将这个式子每一项展开之后可以得到每一项因子,加起来便为约数和。所以求解约数和就变成了求解括号内的每一个通项
 
   定义一个函数$sum(p, k)$,用来求解每一项$ \left ( p_{i}^{0} + p_{i}^{1} + p_{i}^{2} +...+ p_{i}^{k - 1} + p_{i}^{k} \right ) $;
 
  这个式子的求解可以使用递归,在时间复杂度$O(logn)$的情况下求出每一项和,下面是推导过程
 
  • 如果k是奇数,即该式子有偶数项,那么如下:
$$\begin{split} p^{0} + p^{1} + p^{2} +...+ p^{k - 1} + p^{k} &= \left ( p^{0}+ p^{1} + ... + p^{\frac{k}{2}} \right ) + \left ( p^{\frac{k}{2}+ 1} +p^{\frac{k}{2}+ 2}+...+ p^{k}\right )\\ &= \left ( p^{0}+ p^{1} + ... + p^{\frac{k}{2}} \right )+ \left ( p^{\frac{k}{2}+ 1}\times \left ( p^{0}+ p^{1} + ... + p^{\frac{k}{2}}\right ) \right )\\ &= \left ( p^{0}+ p^{1} + ... + p^{\frac{k}{2}} \right )\times \left ( 1+ p^{\frac{k}{2}+ 1}\right ) \end{split}$$
  问题的规模就从$sum(p, k)$转换成了$sum(p, \frac{k}{2})$,递归即可求得;
  同时,观察推导式,我们还需要用快速幂求出$p^{(\frac{k}{2} + 1)}$。
 
  • 如果k是偶数,有奇数项,那么先套用公式计算出$p^0 + p^1 + p^2 + ... + p^{(k - 1)}$,即$sum(p, k - 1)$,然后将算出的数乘$p$,即是$p^1 + p^2 + ... + p^k$的值,然后加上$p^0$ ,即1,得解。
 
#include<bits/stdc++.h>
using namespace std;
#define ll long
const int mod = 9901;
//对于有取模操作的数,x = x * x % mod;
ll getmi(int x, int k) {
    x %= mod;
    ll res = 1;
    while (k) {
        if (k & 1) res = (res * x) % mod;
        x = (x * x) % mod;
        k >>= 1;
    }
    return res;
}

ll sum(int p, int k) {
    if (k == 0) return 1;
    if (k & 1) {
        return (1 + getmi(p, k / 2 + 1)) * sum(p, k / 2) % mod;
    } else {
        return (1 + p % mod * sum(p, k - 1)) % mod;
    }
}

int main() {
    int a, b;
    cin >> a >> b;
    ll ans = 1;
    for (int i = 2; i <= a; i ++) {
        int k = 0;
        while (a % i == 0) {
            k ++;
            a /= i;
        }
        ans = ans * sum(i, k * b) % mod;
    }
    if (!a) ans = 0;
    cout << ans << "\n";
    return 0;
}

 

posted @ 2022-11-23 16:02  Y2ZH  阅读(215)  评论(0)    收藏  举报