题目描述
原题来自:Romania OI 2002
求 A^B 的所有约数之和 mod 9901。
输入格式
输入两个整数 A,B。
输出格式
输出答案 mod9901。
样例
Input | Output |
---|---|
2 3 |
15 |
2^3=8,8 的所有约数为 1,2,4,8,15 mod 9901=15,因此输出 15。
数据范围与提示
对于全部数据,0≤A,B≤5×10^7。
题意: 输出A^B全部因数的和除9901的余数。
分析: 这个问题本身很简单,质因数分解出来,例如分解成num = Πai^bi,全部因数和就是Π(1+ai+ai^2+......+ai^bi)这个乘积式,显然内部是一个等比数列求和,但是众所周知求和时要进行一次除法,虽然模数是质数但分母不一定和模数互质,所以不能用费马小定理求出乘法逆元。
这时有两种选择,第一种是放弃公式求和做法,而是用函数递归一半一半地求和,求出前n/2项和后再乘以q^(n/2)就是后n/2项和,不断向下递归就能得到前n项和,效率也是显然易见的低。
第二种方法其实是除法取余的特殊情况,在b|a的前提下,即a%b == 0时,(a/b)%mod == a%(mod*b)/b,证明如下:
而本题的等比数列求和后一定是整数,所以可以用这个公式,不过大部分情况下b*mod的值会非常大,一般都会大于1e9,在如此大的模数下求快速幂基本都会爆掉long long,所以一些关键值要开__int128。
具体代码如下:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <string>
using namespace std;
const int MOD = 9901;
int a, b, p[100005], n[100005], num;
__int128 mod;
__int128 ksm(__int128 base, int power)
{
__int128 ans = 1%mod;
base %= mod;
while(power)
{
if(power&1) ans = ans*base%mod;
base = base*base%mod;
power >>= 1;
}
return ans;
}
signed main()
{
cin >> a >> b;
//质因数分解
num = 0;//记录不同质因子个数
for(int i = 2; i*i <= a; i++)
{
if(a%i == 0)
{
p[++num] = i;
while(a%i == 0)
{
a /= i;
n[num]++;
}
n[num] *= b;
}
}
if(a > 1)
{
p[++num] = a;
n[num] = b;
}
__int128 ans = 1;
for(int i = 1; i <= num; i++)
{
__int128 b = p[i]-1;
mod = MOD*b;
ans = ans*((ksm(p[i], n[i]+1)-1+mod)%mod/b)%MOD;
}
cout << (int)ans;
return 0;
}