约数和
题目描述
给出a和b求a^b的约数和。
输入格式:
一行两个数a,b。
输出格式:
一个数表示结果对 9901 的模。
Input
2 3
Output
15
SB的思路:
这是一道典型的数论题,本蒟蒻在做的时候首先瞄出a为质数的解法(简直废话,是个人都看得出),
即sum(a,b)=a^0+a^2+a^3+···+a^(b-1)+a^b,然后自以为搞出了什么,结果随手举个反例就Wa了,但是很明显也很容易想到要用快速幂。
然后我又想到洛谷月赛T1,以及一道要用到费马小定理的题目,加上我打出的表,我发现:
a=(a1^k1)*(a2^k2)*···*(an^kn) (其中a1为质因子,k1为该质因子个数)
那么a^b=(a1^k1)^b*(a2^k2)^b*···*(an^kn)^b
再加上质数的约数和的规律我的表
a^b的约数和(暂且用sum表示)就是sum(a,b)=sum(a1,k1*b)*sum(a2,k2*b)*···*sum(an,kn*b)
然后我很愚蠢地使用只快速幂来求约数和,这个想法太愚蠢了我都没脸说(我当时是这样想的:a^b的约数和做两次快速幂就出来了,即在做a^b和a^(b-1)的过程中累加ans值,然后当b-1或b等于0的时候特判一下,本蒟蒻果然没有摸清快速幂的原理。。。),然后又被自己的样例否决了。
想不出正解我就只好暴力,然后在洛谷上水了30分,而我打出的伪正解只有10分
正确思路:
话说我似乎跑偏了,那么上最正确的解题思路(http://www.cnblogs.com/neopenx/p/4094705.html):
事实上最重要的是约数和公式:
①整数唯一分解定理:
一个整数A一定能被分成:A=(P1^K1)*(P2^K2)*(P3^K3).....*(Pn^Kn)的形式。其中Pn为素数。
如2004=(22)*3*167
那么2004x=(22x)*(3x)*(167x)
②约数和公式
对于一个已经被分解的整数A=(P1^K1)*(P2^K2)*(P3^K3).....*(Pn^Kn),
有约数和S=(1+P12+P13+.....P1k1)*.....(1+Pn2+Pn3+.....Pnkn)。
(1+P12+P13+.....P1k1)是一个等比数列,化简为(P1k1+1 -1)/(P1-1),由于有除法同余式,很容易想到乘法逆元。
但是这题和HDU 1452不同,对于逆元表达式ax=1 mod n,乘法逆元存在的条件是gcd(a,n)=1,即a,n互质,但是这题的gcd(P1-1,9901)≠1, 所以不能用乘法逆元求解。
所以有必要对等比数列求和公式改一改:
(1)若n为奇数,一共有偶数项,则:
1 + p + p^2 + p^3 +...+ p^n
= (1+p^(n/2+1)) + p * (1+p^(n/2+1)) +...+ p^(n/2) * (1+p^(n/2+1))
= (1 + p + p^2 +...+ p^(n/2)) * (1 + p^(n/2+1))
上式红色加粗的前半部分恰好就是原式的一半,后半部分递归求解即可。
(2)若n为偶数,一共有奇数项,则:
1 + p + p^2 + p^3 +...+ p^n
= (1+p^(n/2+1)) + p * (1+p^(n/2+1)) +...+ p^(n/2-1) * (1+p^(n/2+1)) + p^(n/2)
= (1 + p + p^2 +...+ p^(n/2-1)) * (1+p^(n/2+1)) + p^(n/2);
这样,在对A质因数分解后,对于每一个质因数,累乘sum(质因数,次数)%mod即可,注意sum计算的时候都要mod防止溢出。
注意一下A的范围,A=0或A=1时无法分解质因数,所以特判结果分别是0和1。
根据以上定理,不难发现这道题非常之水,但是数论太渣,所以公式也没有推到底,再加上自己对快速幂的理解不够深刻,以我的智力是打不出正解的。。。
代码:
1 #include2 #include 3 const long long mod=9901; 4 using namespace std; 5 long long zs[100001],gs[100001]; 6 long long n,m,p,Ans,s; 7 long long mmp[1000001]; 8 void f(long long num) //求a的质因子和该质因子的个数 9 { 10 long long i,j=0; 11 for(i=2;i*i<=num;i++) 12 { 13 if(num%i==0) 14 { 15 long long count=0; 16 zs[++j]=i; 17 while(num%i==0) 18 { 19 count++; 20 num/=i; 21 } 22 gs[j]=count; 23 24 } 25 } 26 if(num>1) 27 { 28 zs[++j]=num; 29 gs[j]=1; 30 } 31 s=j; 32 } 33 long long ksm(long long a,long long b) //快速幂,注意要边乘边模 34 { 35 long long ans=1,base=a; 36 long long f=0; 37 while(b>0) 38 { 39 if(b%2!=0) 40 ans=(ans*base)%mod; 41 base=(base*base)%mod; 42 b/=2; 43 } 44 return ans; 45 } 46 long long sum(long long p,long long k) //递推求约数和公式前一部分 47 { 48 if(k==0) return 1; 49 if(k%2) return ((sum(p,k/2)*(1+ksm(p,k/2+1)))%mod); 50 else return ((sum(p,k/2-1)*(1+ksm(p,k/2+1))+ksm(p,k/2))%mod); 51 } 52 int main() 53 { 54 scanf("%lld%lld",&n,&m); 55 Ans=1; 56 if(m==0) //特判一下 57 { 58 printf("1"); 59 return 0; 60 } 61 f(n); 62 for(int i=1;i<=s;i++) 63 { 64 Ans=(Ans*(sum(zs[i],gs[i]*m)%mod))%mod; //约数和公式 65 } 66 printf("%lld",Ans); 67 return 0; 68 }
这道题似乎有三种做法,大部分是用的以上这种,毕竟很好想到,若是知道约数和公式就非常轻松了,不知道约数和公式也可以推出来(像我这种地表最弱蒟蒻就算了)
还有一种较为常见的就是逆元,但我一个同学说他直接用快速幂求sum(a1,(k1^b)%mod)过了。。。
这篇博客我逼逼得有点多,见谅。