约数之和

题目

题目

题解

思路很神奇,把(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
");
	else  if(n==1  ||  m==0)printf("1
");
	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
",ans);
	}
	return  0;
}
原文地址:https://www.cnblogs.com/zhangjianjunab/p/13394989.html