题目描述
求 ABAB 的所有约数之和 mod9901mod9901。1≤A,B≤5×1071≤A,B≤5×107。
算法分析
将 AA 分解质因数得 ,则 AB=pc0B0pc1B1...PcnBnAB=p0c0Bp1c1B...PncnB,它的约数集合就是 {pk00pk11...Pknn|0≤ki≤ciB}{p0k0p1k1...Pnkn|0≤ki≤ciB}。
根据乘法原理,ABAB 的约数之和为 (p00+p10+...+pc0B0)×(p01+p11+...+pc1B1)×...×(p0n+p1n+...+pcnBn)(p00+p01+...+p0c0B)×(p10+p11+...+p1c1B)×...×(pn0+pn1+...+pncnB)。
上面乘法运算的每个因数都是一个等比数列,但由于公式涉及除法,而取模运算对除法不满足分配率,因此不能用等比数列求和公式求解。
我们考虑分治,将 x0+x1+...+xnx0+x1+...+xn 拆成 x0+x1+...+x⌊n−12⌋+(x0+x1+...+x⌊n−12⌋)×x⌊n−12⌋+1x0+x1+...+x⌊n−12⌋+(x0+x1+...+x⌊n−12⌋)×x⌊n−12⌋+1,若元素个数是奇数,则还要再乘上 xnxn,这样就把问题规模缩小了一半,配合快速幂,可以在 O(log2n)O(log2n) 的时间复杂度求解。
实现时要提高分解质因数的效率,枚举到 n‾√n 然后特判比枚举到 nn 要快,还要注意乘法运算可能溢出,使用 位整数储存中间结果。
代码实现
#include <cstdio>
#include <vector>
const int mod=9901;
std::vector<int> prime;
std::vector<int> times;
void divide(int num) {
for(int i=2,end=num;i*i<=end;++i) {
if(num%i==0) {
prime.push_back(i);
int cnt=0;
while(num%i==0) {
++cnt;
num/=i;
}
times.push_back(cnt);
}
}
if(num>1) {
prime.push_back(num);
times.push_back(1);
}
}
int pow(int n,int k) {
int ans=1;
while(k) {
if(k&1) ans=ans*n%mod;
n=(long long int)n*n%mod;k>>=1;
}
return ans;
}
int powsum(int n,int k) {
if(k==0) return 1;
int temp=powsum(n,(k-1)/2);
int ans=(temp+temp*pow(n,(k-1)/2+1)%mod)%mod;
if(!(k&1)) ans=(ans+pow(n,k))%mod;
return ans;
}
int main() {
int a,b;scanf("%d%d",&a,&b);
divide(a);
for(int i=0,end=times.size();i<end;++i) times[i]*=b;
int ans=1;
for(int i=0,end=prime.size();i<end;++i) ans=ans*powsum(prime[i],times[i])%mod;
printf("%d\n",ans);
return 0;
}