首先我们来读题
Consider two natural numbers A and B. Let S be the sum of all natural divisors of A^B. Determine S modulo 9901 (the rest of the division of S by 9901).
再注意一下数据范围
中文意思也不难理解
求的所有约数之和
题目很短,也没有什么挖坑的地方.
分析
把进行质因数分解,可表示为
则可表示为
不难理解,的所有约数可用集合表示.
其实就是所有取值的组合.这是一个很明显的母函数思想
进而可将的所有约数之和转化为
解法1:
对于式,其中的每一项都是一个等比数列,我们可以利用等比数列的通向公式求解,但涉及到取模和除法的问题,这里需要一些特殊的转化.
还可以想到一个方法就是等比数列其实可以利用分治的方法求解.
对于等比数列,当n为奇数(即含有偶数项)时,可将其转化为,其实就是对后半段提取了一个公因式,
n为偶数时也同理.
我们可以根据这种转化来将大问题转化为小问题,也就是分治.
解法2:
用等比数列的通项公式快速求解,但是需要注意很多细节,比如运算中设计除法,数值较大容易溢出等.
这里直接引入逆元里的变换模值方法:
变换模值:。这种方法是用于c和b小的时候,这题就刚刚好。
接下来可以看代码了,给出了两种解法,并在难理解的地方添加了注释
//解法1 采用分治法在O(logn)的复杂度计算等比数列以求解
using namespace std;
const int MAXN = 2 * 60000 + 5;
const int MOD = 9901;
typedef long long ll;
inline ll qpow(ll a, ll b) //计算a^b
{
ll res = 1;
a %= 9901; //这里要先取模 不然可能溢出
while (b > 0) {
if (b & 1) {
res = (res * a) % MOD;
}
a = (a * a) % MOD;
b >>= 1;
}
return res;
}
ll calc(ll p, ll c) //1+p^1+p^2+...+p^c
{
if (c == 0)
return 1;
if (c == 1)
return p + 1;
if (c & 1) {
return calc(p, c / 2) * (qpow(p, c / 2 + 1) + 1) % MOD; //见上面分析
}
return (calc(p, c - 1) + qpow(p, c)) % MOD; //若n为偶数,取前n-1项直接转化为n为奇数的情况
}
int main (){
ios::sync_with_stdio(false);
map<ll, ll> mp;
ll a, b;
cin >> a >> b;
if (b == 0) {
cout << 1 << endl;
return 0;
}
for (int i = 2; i * i <= a; ++i) { //进行质因数分解,mp保存的是质因数的值和个数
if (a % i == 0) {
int cnt = 0;
while (a % i == 0) {
a /= i;
++cnt;
}
mp[i] = cnt;
}
}
if (a > 1) //对应a为质数的情况
mp[a] = 1;
ll ans = 1;
for (map<ll, ll>::iterator it = mp.begin(); it != mp.end(); ++it) {
ans = (ans * calc(it->first, it->second * b)) % MOD;
}
cout << ans << endl;
return 0;
}
//解法2 除法取模 变换模值
const int MAXN = 2 * 60000 + 5;
const int MOD = 9901;
typedef long long ll;
inline ll qmulti(ll a, ll b, ll MOD) //解决直接乘法导致的溢出
{
ll res = 0;
a %= MOD;
while (b > 0) {
if (b & 1) {
res = (res + a) % MOD;
}
a = (a + a) % MOD;
b >>= 1;
}
return res;
}
inline ll qpow(ll a, ll b, ll MOD) //a^b
{
ll res = 1;
a %= MOD; //这里要先取模 不然可能溢出
while (b > 0) {
if (b & 1) {
res = qmulti(res, a, MOD); //这里如果直接用*,则会溢出,只能用自己实现的快速乘法取模
}
a = qmulti(a, a, MOD);
b >>= 1;
}
return res;
}
int main (){
ios::sync_with_stdio(false);
map<ll, ll> mp;
ll a, b;
cin >> a >> b;
if (b == 0) {
cout << 1 << endl;
return 0;
}
if (a == 0) {
cout << 0 << endl;
return 0;
}
for (ll i = 2; i * i <= a; ++i) {
if (a % i == 0) {
int cnt = 0;
while (a % i == 0) {
a /= i;
++cnt;
}
mp[i] = cnt;
}
}
if (a > 1)
mp[a] = 1;
ll ans = 1;
for (map<ll, ll>::iterator it = mp.begin(); it != mp.end(); ++it) {
ll mod = MOD * (it->first - 1);
ll a = (qpow(it->first, it->second * b + 1, mod) - 1 + mod) % mod;
ll b = it->first - 1;
ans = qmulti(ans, (a / b), MOD);
}
cout << ans << endl;
return 0;
}