版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/hqddm1253679098/article/details/81034783
题目:
POJ1845
分析:
首先用线性筛把
分解质因数,得到:
则显然 分解质因数后为
接下来隆重推出约数和定理:(证明见【知识总结】约数个数定理和约数和定理及其证明)
那么很明显可以对于每一个 计算 然后乘起来就是答案。这就是一个等比数列求和了。
等比数列求和公式中含有除法,所以取模求和的时候不能直接用求和公式,否则如果除数刚好是模数的倍数就会出现逆元不存在的尴尬情况……例如POJ该题讨论区中的数据 ( ,求和公式中除数是 ,此数在模 意义下没有逆元)
这里介绍一种二分等比数列求和的方法,思路和快速幂相似,即代码中的
函数
可以把这个等比数列平分成长度相等的两部分。
当
是偶数
然后从后半部分提出一个 ,它就和偶数部分一样了!得到
显然左边可以递归地算下去,右边用快速幂求出。
当 是奇数,只要上述 均向下取整,算完以后加上 就可以了。这个也可以用快速幂解决。
代码:
函数求的是
,所以最后统计答案的时候要手动加上
(也就是
)
筛质数时有一个小技巧。并不需要筛出
范围内的所有质数。x不可能含有两个或以上大于
的质因数,所以
除以
范围内的所有质数后如果仍不为
,那么此时剩下的
一定是一个质数
我才不会告诉你模数叫QQ_kotori是为了膜某位n姓QQ小嘴/复读机
#include <iostream>
using namespace std;
namespace zyt
{
typedef long long ll;
const int QQ_kotori = 9901;
const int M = 7100;
ll prime[M], index[M];
int cnt;
void init_prime(ll x)
{
static bool mark[M];
for (int i = 2; i < M && x > 1; i++)
{
if (!mark[i])
{
ll tmp = 0;
prime[cnt] = i;
while (x % i == 0)
tmp++, x /= i;
index[cnt++] = tmp;
}
for (int j = 0; j < cnt && i * prime[j] < M; j++)
{
ll k = i * prime[j];
mark[k] = true;
if (i % prime[j] == 0)
break;
}
}
if (x > 1)
{
prime[cnt] = x;
index[cnt++] = 1;
}
}
ll power(ll a, ll b)
{
ll ans = 1;
while (b)
{
if (b % 2)
ans = ans * a % QQ_kotori;
a = a * a % QQ_kotori;
b /= 2;
}
return ans;
}
ll powersum(ll a, ll b)
{
if (b == 1)
return a % QQ_kotori;
ll ans = powersum(a, b / 2) * (1 + power(a, b / 2)) % QQ_kotori;
if(b % 2)
ans = (ans + power(a, b)) % QQ_kotori;
return ans;
}
void work()
{
ll a, b, ans = 1;
cin >> a >> b;
if (a == 0)
{
cout << 0;
return;
}
else if (b == 0)
{
cout << 1;
return;
}
init_prime(a);
for (int i = 0; i < cnt; i++)
if (index[i])
ans = ans * (powersum(prime[i], index[i] * b) + 1) % QQ_kotori;
cout << ans;
}
}
int main()
{
zyt::work();
return 0;
}