POJ 1845 (洛谷 :题目待添加)Sumdiv

约数和

题目描述

给出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 #include<iostream>
 2 #include<cstdio>
 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)过了。。。

这篇博客我逼逼得有点多,见谅。

 

   

转载请注明出处:http://www.cnblogs.com/drurry/
原文地址:https://www.cnblogs.com/drurry/p/7668213.html