快速幂是个快速计算幂次的算法,原理很简单:

 a^b=a*a*a*a*a*……*a (b个a 相乘) 如果直接计算要做 b-1 次乘法。

可以利用幂运算的性质: a^b=(a^c)*(a^d)=a^(c+d)  其中b=c+d

如果把b平均分为两份,每份的数量为c=d=b/2, a^c和a^d的值一样,就可以重复利用。

这样分一次就只要做 b/2-1+1=b/2次的乘法,若b为奇数,则要做b/2+1次乘法,于是乘法的次数就大大减少,

这样重复分几次后,原式的值就能很快地算出来。

用递归的代码:

1 int QuickPow(int a,int b)
2 {
3      if (b==1) return a;
4      if (b&1) return QuickPow(a,b-1)*a;   //b为奇数
5      return QuickPow(a,b/2);
6 }

非递归:

 1 int QucikPow(int a,int b)
 2 {
 3     int ans=1;
 4     while (b)
 5     {
 6          if (b&1) ans=ans*a;
 7          b=b>>1;
 8          a=a*a;
 9      }
10     return ans;
11 }

注意事项:

     1.用递归的方法会好理解点,但如果b很大的话递归会爆栈。

    2. 函数返回值可能会溢出,如题目要求可利用模运算规则:(a*b)%m=(a%m*b%m)%m

    3. 就算取模了,算法中有乘法操作的地方也还可能会溢出,这就要用快速幂的变式:

           快速加法: a*b=a*(b/2)+a*(b/2)

                          (a+b)%m=(a%m+b%m)%m

           这样就不会溢出了。(还是在tyvj p2043 发现的,下面附上AC代码)

 1 #include <stdio.h>
 2 unsigned long long p,m,n;
 3 unsigned long long ans=1;
 4 inline unsigned long long quickadd(unsigned long long a,unsigned long long m)
 5 {
 6    unsigned long long ans=0;
 7    while (a)
 8    {
 9        if (a&1) ans=(ans+m)%p;
10        a=a>>1;
11        m=(m*2)%p;
12    }
13    return ans;
14 }
15 void quick(unsigned long long n)
16 {
17   while (n)
18   {
19       if (n&1) ans=(quickadd(ans,m))%p;
20       n=n>>1;
21       m=(quickadd(m,m))%p;
22   }
23 }
24 
25 int main()
26 {
27   scanf("%I64d%I64d%I64d",&n,&m,&p);
28   ans=m%p;
29   m=m-1;
30   m=m%p;
31   quick(n-1);
32   printf("%I64d\n",ans);
33 }