POJ1845-Sumdiv大数约数和

题目链接:http://poj.org/problem?id=1845

题目大意:

  求A^B的所有约数和s。A和B都很大(0<=A,B<=50000000).

题目分析:

  这道题让我学会了很多东西,很多我之前没有见识过的专属于数学的技巧之类的。看了别人的博客,受益匪浅。现总结如下:

  先来分析一下这道题目,再来讲技巧吧。

  这道题真是大数中的大数。刚看到这道题就完全没有头脑。全凭小优的指点(http://blog.csdn.net/lyy289065406) 。

  (1) 整数唯一分解定理 

  任何整数都可以且尽可以分解成若干个素数相乘的形式,如下:

  A=p1^k1 * p2^k2 *……* pn^kn;其中的pi各表示一个素数.

  (2)约数和公式

  按照整数的分解定理,我们有一个整数的约数之和公式为:

  S=(1+p1+p1^2+……p1^k1) * (1+p2+p2^2+……+p2^k2)  *……*  (1+pn+pn^2+……+pn^kn).

  (3) 同余模公式

            (a+b)%mod=(a%mod + b%mod)%mod;

     (a*b)%mod=(a%mod * b%mod)%mod;

  *以上的三个数学知识是解这道题的关键。请先消化理解以后再往下面看*

  第二部份呢,我们来利用上面的理论基础分析一下这道题。

  如果有:A=p1^k1 * p2^k2 *……* pn^kn;

  那么有:A^B=p1^(k1*B) * p2^(k2*B) *……* pn^(kn*B);

  因而   : S=[1+p1+p1^2+……p1^(k1*B)]  *  [1+p2+p2^2+……+p2^(k2*B)]  *……*  [1+pn+pn^2+……+pn^(kn*B)].

  至此,答案已经很明了了,下面的事情就是如何来用程序实现上面的过程了,注意,最终的S是要mod 9901 的哦!

  *还有什么不明白的吗?可以在讨论区提问哦*

  第三部份,我们来尝试用程序实现上述公式:

  (1)质因数分解

  在这里我用的根号法+递归法(名字是小优那里借鉴的),起初还没想明白这个方法,试图用素数表的方式来解决问题,看到五千万那么大就没有敲了,如果感兴趣,可以自己试一试哦。看一看下面的“根号法+递归法”吧。

for(int i=2;i*I<=A;) //根号法的体现
{
    int k=0;//这是p[ ]和n[ ]的指针。p是存素因子的,n是存其指数
    if (A%i==0)
    {
        p[k]=i;
        n[k]=0;
        while(A%i) //这是递归法的体现,找到一个素因子就用此法计算其个数
        {
            n[k]++;
            A/=i;
        }
        k++;
    }
    if(i==2) //这是所谓的奇偶法,除了2其余素数都是奇数哦
         i++;
    else
        i+=2;
}

if(A!=1)//常规来讲,这时候的A已经被分解剩下1了,除非A本身就是素数
{
    p[k]=A;
    n[k++]=1;
}

  乍看上去,这也太慢了吧,怎么一个个去试,会不会出现重复啊?会不会把非质数当成是质因子啊?这个问题呢就留给大家自己思考啊。*

  

  (2)二分法求等比数列的和

  解决了A的分解问题,自然需要来解决一下S的求解问题,很明显S是一系列以pi为公比的等比数列和 之积。只要能解决等比数列和的问题那这道题就迎刃而解了啊。最最直接的方法是利用求和公式,但是别忘了,我们的S可是还需要对9901取模的,[pi^(ki*B)-1]/(pi-1)这个结果中pi-1未必和9901互素!因而,解决这个问题,就只好用二分法:

   对于一个等比数列求和  S=1+q+q^2+……q^n

   如果n为奇数,那么一共就有偶数个项了,

     S=[1+q+……+q^(n/2)] * [1+q^(n/2+1)]

   如果n为偶数,那么一共就是有奇数个项了,

   S=[1+q+……+q^(n/2-1] * [1+q^(n/2+1)]+q^(n/2)

  *如果记不住上面的公式的话,就举个例子自己算算,结果自然就很清楚了。*

  现在所有的理论问题都解决了,就看代码吧。

 1 #include<iostream>
 2 #include<cstdio>
 3 using namespace std;
 4 #define size 10000
 5 #define mod 9901
 6 
 7 int p[size],n[size];
 8 
 9 long long pow3(long long int a,long long int b )//快速幂
10 {
11     long long int r = 1, base = a;
12     while( b != 0 )
13     {
14         if( b & 1 )
15             r =(r * base) % mod;
16         base =(base * base) % mod;
17         b >>= 1;
18     }
19     return r;
20 }
21 
22 long long sum(long long p,long long n)//二分递归求解等比数列之和
23 {
24     if(n==0)
25         return 1;
26     if(n%2)
27         return ((sum(p,n/2) % mod) * (1+pow3(p,n/2+1))% mod )% mod;
28     else
29         return (((sum(p,n/2-1) % mod) * (1+pow3(p,n/2+1))% mod ) % mod + pow3(p,n/2) %mod)%mod;
30 }
31 
32 int main()
33 {
34     int A,B;
35     while(scanf("%d%d",&A,&B)!=EOF)
36     {
37         int js=0;
38         for(int i=2;i*i<=A;)//质因数分解A,根号法和递归法
39         {
40             if(A%i==0)
41             {
42                 p[js]=i;
43                 n[js]=0;
44                 while(A%i==0)
45                 {
46                     n[js]++;
47                     A/=i;
48                 }
49                 js++;
50             }
51             if(i==2)
52                 i++;
53             else
54                 i+=2;
55         }
56         if(A!=1)//A本身就是素数的
57         {
58             p[js]=A;
59             n[js++]=1;
60         }
61 
62         long long ans=1;
63         for(int i=0;i<js;i++)
64         {
65             ans=( ans * sum(p[i],n[i]*B)%mod )%mod;
66         }
67         cout<<ans<<endl;
68     }
69     return 0;
70 }
POJ1845
原文地址:https://www.cnblogs.com/xiaozhuyang/p/POJ1845-Sumdiv.html