题目描述
输入两个整数 a 和 b,求 aba^bab 的因子和。
由于结果太大,只要输出它对 9901 取模的结果。
输入格式
仅一行,为两个整数 a和 b。
输出格式
输出一行一个整数表示答案对 9901 取模的结果。
输入输出样例
输入 #1复制
2 3
输出 #1复制
15
说明/提示
数据规模与约定
对于全部的测试点,保证 1≤a≤5×10710^7107,1≤b≤5×10710^7107 。
解题思路
1、{分解质因子}
首先,看到题目要求的数据,就已经确定是无法使用暴力的,那么根据以往的经验,我们就要求解整数a的质因子,并求解出每个质因子的个数,然后再让该质因子的个数乘以b就是对于aba^bab这个数中分解出来的每一个质因子的个数。
eg:
414^141中 质因子2的个数就是2*1=2个
424^242中 质因子2的个数就是2∗22*22∗2=4个
434^343中 质因子2的个数就是2∗32*32∗3=6个
…
(本题使用数组进行存储即可)
2、{等比数列求和}
然后我们对求解出的质因子进行分析,不难发现,假设当前质因数为m,质因数的个数为n,那么mnm^nmn,肯定aba^bab的一个因子而且是以m为底数的最大因子。
eg:
16能分解的因子分别是:1,2,4,8,16.
由上述思路1可知16的质因子的个数是4,也就是我们以2为底数最大的该数的因子就是242^424=16,与此同时,我们可以发现16的因子和就等于=1+212^121+222^222+232^323+242^424。
也就是说对于aba^bab可以分解的质因数,这个数因子和的一部分就等于m从0到n次幂的和。
综上所述,我们求解该数的因子和就等于(借图一用,感谢!)
这里的pi就相当于可以拆解的质因子,ci就代表该数可以分解得到的当前之因子的个数。
看到这里,结合高中的知识,就将问题转化为等比数列求和。
等比数列求和公式:
在本题中a1均为1,q代表质因子的值,n代表每一个质因子的个数。
将相关数据代入 q=m(质因子的值),n(等比数列的项数)=n(质因子的个数)+1。进而推出:
看到上面的pici+1pi^{ci+1}pici+1我们就会想到快速幂,直接套用模板就可以。
3、{费马小定理求逆元}
最后,我们看题中所给的条件,题目中让我们对结果数对9901进行取余,为什么要对这么特别的数字进行取余呢?可以发现9901是一个素数,再观察上面等比数列求和的公式,发现我们还有 /(pi-1)没有进行处理,这时就要想到求逆元。
也就是说除以一个数,换做乘以这个数的倒数的结果是不变的,再根据费马小定理的公式有:
带入公式求解快速幂即可。
注意
1、首先,为了防止取模的时候是负数,可以先将其+mod再进行取模。
2、为什么求解等比数列求和的每一次求解第一项都是1而不是m(m代表当前质因子)?
这也是我最初没有想明白的地方。后来找到了题解中的这一部分:(为大佬点赞!!!)
3、为什么要考虑质因子 (m%9901)==1是否成立?
因为如果有个质因子m,m%9901=1,那用费马小定理求逆元的时候,(m-1)%9901=0,这时候不存在逆元(0是没有倒数的),所以会出错。那么试想,我们m%9901=1,那式子(1+m+m2m^2m2+…+mcntm^{cnt}mcnt) 是不是就等于cnt+1?(因为里面每一项mim^imi(0<=i<=n)mod 9901之后都是1),所以计算需要特判!!!
完整代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=9901;
int yin[10010][2];//0下标对应因子,1下标代表该因子的个数
int cnt=0;
ll qpow(ll a,ll b)//矩阵快速幂
{
ll res=1;
while(b)
{
if(1&b) res=((ll)a*res)%mod;
a=(ll)a*a%mod;
b>>=1;
}
return res;
}
int main()
{
ll m,n;
scanf("%lld %lld",&m,&n);
for(int i=2;i<=m/i;i++)//求解该数的质因子
{
if(m%i==0)
{
while(m%i==0)
{
m/=i;
yin[cnt][1]++;
}
yin[cnt][0]=i;
yin[cnt][1]*=n;//因为是要求m的n次幂,所以我们在此处因子的个数时也要乘以相应的倍数
cnt++;
}
}
if(m>1)//该质因子大于根号n
{
yin[cnt][0]=m;
yin[cnt][1]=n;
cnt++;//为了下面的循环,必须要让在循环数组的下标小于cnt,因此需要进行++
}
ll ans=1;
for(int i=0;i<cnt;i++)
{ //前半部分求解的是等比数列求和,为了防止是负数,要加上mod在进行取余
ll t=(qpow(yin[i][0],yin[i][1]+1)+mod-1)%mod*qpow(yin[i][0]-1,mod-2)%mod;//第二部分是使用费马小定理求解q-1的逆元
if(yin[i][0]%mod==1)//如果说因子对9901取余的余数是1,那么该因子-1对9901取余的结果就是0,那么此时就不存在逆元,此时就变成因子个数+1个1相加
{
ans=ans*(yin[i][1]+1)%mod;
}
else ans=ans*t%mod;
}
printf("%lld",ans);
return 0;
}