约数之和
作者:互联网
题目
题解
思路很神奇,把\(A\)分解成\(a_1^{b_1}*a_2^{b_2}*a_3^{b_3}*...*a_n^{b_n}\)(\(a_i\)为质数)。
那么,我们现在就得到了他的质因数以及每个质因数的个数了,而它的约数肯定是在其中选择的几个数字相乘得到的,换个角度想,其实就是对于每个质因数选择多少个的问题。
不难想到,当选\(0\)个\(a_i\)和选\(j\)个\(a_i\)时的约数个数是一样的,而且在除去了\(a_i\)这些约束甚至还是一样的。
用乘法分配律不难得出约数之和是\((a_1^0+a_1^1+a_1^2+a_1^3+...+a_1^{b_1*B})*...*(a_n^0+a_n^1+a_n^2+a_n^3+...+a_n^{b_n*B})\)
而且对于每个括号内,是一个等比数列,以\(a_1\)为例子,公式为:\(\frac{a_1^{b_1*B+1}-1}{a_1-1}\),但是需要注意的是,如果\(a_i\%9901=1\)的话,这个公式就会废掉,需要特判,当然,因为涉及到了除法,所以需要乘法逆元,而\(9901\)是个质数,所以\(x^{9899}\)就是\(x\)的乘法逆元了,这些都只需要快速幂就可以解决了。
代码
#include<cstdio>
#include<cstring>
#include<cmath>
#define N 2100000
using namespace std;
typedef long long LL;
LL n,m,p=9901;
LL a[N]/*约数*/,b[N]/*次数*/,top;
inline LL ksm(LL x,LL y)
{
LL ans=1;x%=p;
while(y)
{
if(y&1)ans=(ans*x)%p;
x=(x*x)%p;y>>=1;
}
return ans;
}
LL dfs(LL x,LL y)
{
a[++top]=y%p;
while(x%y==0)b[top]++,x/=y;
return x;
}
void fenjie(int x)
{
int y=sqrt(x);
for(LL i=2;i<=y && i<=x/*其实如果把后面的if去掉然后改成i<=x也对,但是会慢*/;i++)
{
if(x%i==0)x=dfs(x,i);
}
if(x!=1)
{
a[++top]=x;
b[top]=1;
}
}
int main()
{
scanf("%lld%lld",&n,&m);
if(n==0)printf("0\n");
else if(n==1 || m==0)printf("1\n");
else
{
fenjie(n);
for(int i=1;i<=top;i++)b[i]*=m;
LL ans=1;
for(int i=1;i<=top;i++)
{
if(a[i]==1)ans=(ans*(b[i]+1))%p;
else ans*=((ksm(a[i],b[i]+1)+p-1/*+p防止<0*/)%p)*ksm(a[i]-1+p/*防止<0*/,p-2),ans%=p;
}
printf("%lld\n",ans);
}
return 0;
}
标签:约数,9901,LL,ans,include,top 来源: https://www.cnblogs.com/zhangjianjunab/p/13394989.html