题目来源:AcWing 97. 约数之和
题目描述
假设现在有两个自然数 和 , 是 的所有约数之和。
请你求出 mod 的值是多少。
输入格式
在一行中输入用空格隔开的两个整数 和 。
输出格式
输出一个整数,代表 mod 的值。
数据范围
- 和 不会同时为0
输入样例
2 3
输出样例
15
解题思路
计算约数的和
为了简化问题,我们先来看如何对求约数之和。设存在质数为A的质因数,则对于有:
以质因数为例,拥有个,故对于质因数存在种选法,组成不同的因数。因此,的因数个数的值为(约数个数定理):
由的因数个数和乘法原理,则可以得到的约数之和的值(约数和定理):
其中,任意的均为的约数。将该式推广,则可以得到的值:
其中,任意的均为的约数,每一项对应一个质因数,分别对每一项求和并累乘即可得到结果。根据上述思路,我们给出相应的代码。
int main() {
int a, b;
cin>>a>>b;
int res = 1;
for(int x = 2; x <= a; x++) { // 求质因数
int cnt = 0;
while(a % x == 0) {
cnt += 1;
a /= x;
}
res = res * sum(x, cnt * b) % mod; // 拥有b*cnt个质因数x
}
if(a == 0) res = 0;
cout<<res<<endl;
return 0;
}
分治求解每一项的和
对于质因数的项,使用分治法求和能够将时间复杂度压缩到。
每一项从处分开,则原式被分为两部分,分别为前式与后式。
当为偶数时,从后式中提出公因子,则后式变为,整理上述式子。
设,则当为偶数时,有:
当为奇数时,则将减去1变为偶数,有:
我们给出对应的代码。
int sum(int p, int k) {
if(k == 0) return 1;
if(k % 2 == 0) return (p % mod * sum(p, k - 1) + 1) % mod;
return (1 + qmi(p, k / 2 + 1)) * sum(p, k / 2) % mod;
}
快速幂求公因子
为了方便描述,我们将公因子简化为。对任意的整数,其一定可以转化为对应的二进制表示。设对应的二进制表示中的各个位都为1,则每一位二进制位都有值。其中,为当前二进制位对应的值,为前一位二进制位对应的值。
当二进制位为1时,累加当前值,当二进制位为0时,不加当前值。我们给出对应的代码。
int qmi(int p, int k) {
p %= mod;
int res = 1;
while(k) {
if(k & 1) res = res * p % mod;
p = p * p % mod;
k >>= 1;
}
return res;
}
参考代码
#include<iostream>
using namespace std;
const int mod = 9901;
int qmi(int p, int k) {
p %= mod;
int res = 1;
while(k) {
if(k & 1) res = res * p % mod;
p = p * p % mod;
k >>= 1;
}
return res;
}
int sum(int p, int k) {
if(k == 0) return 1;
if(k % 2 == 0) return (p % mod * sum(p, k - 1) + 1) % mod;
return (1 + qmi(p, k / 2 + 1)) * sum(p, k / 2) % mod;
}
int main() {
int a, b;
cin>>a>>b;
int res = 1;
for(int x = 2; x <= a; x++) {
int cnt = 0;
while(a % x == 0) {
cnt += 1;
a /= x;
}
res = res * sum(x, cnt * b) % mod;
}
if(a == 0) res = 0;
cout<<res<<endl;
return 0;
}