约数的基础知识:点击这里
把 A A A 分解质因数,表示为 p 1 c 1 ∗ p 2 c 2 ∗ . . . ∗ p n c n p_1^{c_1}*p_2^{c_2}*...*p_n^{c_n} p1c1∗p2c2∗...∗pncn
那么 A B A^B AB 可表示为 p 1 B ∗ c 1 ∗ p 2 B ∗ c 2 ∗ . . . ∗ p n B ∗ c n p_1^{B*c_1}*p_2^{B*c_2}*...*p_n^{B*c_n} p1B∗c1∗p2B∗c2∗...∗pnB∗cn。
A B A^B AB 的所有约数可表示为集合 { p 1 k 1 ∗ p 2 k 2 ∗ . . . ∗ p n k n } \left\{p_1^{k_1}*p_2^{k_2}*...*p_n^{k_n}\right\} {p1k1∗p2k2∗...∗pnkn},其中 0 ≤ k i ≤ B ∗ c i ( 1 ≤ i ≤ n ) 0≤k_i≤B*c_i\ (1≤i≤n) 0≤ki≤B∗ci (1≤i≤n)。
根据乘法分配律, A B A^B AB 的所有约数之和就是:
( 1 + p 1 + p 1 2 + . . . + p 1 B ∗ c 1 ) ∗ ( 1 + p 2 + p 2 2 + . . . + p 2 B ∗ c 2 ) ∗ . . . ∗ ( 1 + p n + p n 2 + . . . + p n B ∗ c n ) (1+p_1 +p_1^2+...+p_1^{B*c_1})*(1 +p_2+p_2^2+...+p_2^{B*c_2})*...*(1+p_n+p_n^2+...+p_n^{B*c_n}) (1+p1+p12+...+p1B∗c1)∗(1+p2+p22+...+p2B∗c2)∗...∗(1+pn+pn2+...+pnB∗cn)
上式中的每个括号内都是等比数列,如果使用等比数列求和公式,需要做除法。而答案还需要对 9901 9901 9901 取模,mod 运算只对加、减、乘有分配率,不能直接对分子分母分别取模后再做除法。
我们可以换一种思路,使用分治法进行等比数列求和。所谓分治法,就是把一个问题划分为若干个规模更小的同类子问题,对这些子问题递归求解,然后在回溯时通过它们推导出原问题的解。
问题:使用分治法求 s u m ( p , c ) = 1 + p + p 2 + . . . + p c = ? sum(p,c)=1+p+p^2 +...+p^c= ? sum(p,c)=1+p+p2+...+pc=?
若 c c c 为奇数:
若 c c c 为偶数,类似地:
每次分治(递归之后),问题规模均会缩少一半,配合快速幂即可在 O ( l o g c ) O(logc) O(logc) 的时间内求出等比数列的和。
#include
#include
#include
#include
#include
using namespace std;
const int mod = 9901;
int Pow(int a, int b)
{
a %= mod;
int res = 1;
while(b)
{
if(b&1)
res = res * a % mod;
a = a * a % mod;
b >>= 1;
}
return res;
}
int sum(int p, int c)
{
if(c==0)
return 1;
if(c&1)
return (1 + Pow(p, (c+1)/2)) * sum(p, (c-1)/2) % mod;
else
return (Pow(p, c) + (1 + Pow(p, c/2)) * sum(p, c/2-1)) % mod;
}
int main()
{
int A, B;
scanf("%d%d", &A, &B);
int ans = 1;
for(int i = 2; i <= A; ++i)
{
int cnt = 0; //A里面有cnt个因子i
while(A % i == 0)
{
cnt++;
A /= i;
}
if(cnt)
ans = ans * sum(i, cnt * B) % mod;
}
if(!A) ans = 0;
printf("%d\n", ans);
return 0;
}