/*
*/
把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【模板】快速幂

(还记得大明湖畔的快速幂吗?)

先提一个小问题,怎么求a的n次方?

当然是暴力。。直接乘就好啊。。

等等,当n=10e+9怎么办?(当然我知道这玩意一般还要再模一个c)

难不成你还想。。(不你不想)

这时,蒟蒻默默打出一个问号。。(所以我去学了快速幂)

————快速幂

原理:

首先,有这样一个公式

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

(a^b)%c=((a%c)^b)%c

长得好奇怪。。但其实和实数运算法则一样。。

这样就有下面的第一步优化,为了使求出来的值不超long long 

int ans = 1;
a = a % c;  //加上这一句
for(int i = 1;i<=b;i++)ans = ans * a;
ans = ans % c;

(但是这样也还是会超。。)

再看一下上面的公式,既然先相乘再取余先取余再相乘再取余一样,那就可以给每一步的结果ans取余。

int ans = 1;
a = a % c; 
for(int i = 1;i<=b;i++)ans = (ans * a) % c;//这里再取了一次余
ans = ans % c;

这时的时间复杂度为O(b)

虽然现在不会溢出,但是当b很大的时候还是会超时

然后,有这样一个公式

a^b%c==((a^2)b/2)%c == ((a^2 % c)b/2)%c          b为偶数

a^b%c==(a*(a^2)b/2)%c == (a*(a^2 % c)b/2)%c         b为奇数

这样,就可以进一步优化为:

int ans = 1;
a = a % c;
k = (a*a) % c;               //相当于用k代替了a2
if(b%2==1)
    ans = (ans * a) % c; //如果是奇数,要多求一步,可以提前算到ans中
for(int i = 1;i<=b/2;i++) ans = (ans * k) % c;
ans = ans % c;

这时的时间复杂度是O(b/2)

还是远远不够啊。。

但是,由上面的一次优化可以发现,我们已经把a^b%c转换成了k^(b/2)%c

而K^(b/2)%c是可以继续迭代下去的

例如,

下一步再令 x=(k*k)%c 偶数就是求 x^b/4 % c 奇数就是 (x^b/4 % c*a)% c
再下一步令 y=(x*x)%c 偶数就是求 y^b/8 % c 奇数就是 (y^b/8 % c*a)% c
    ......  直到指数b ==0
当然,对于奇数的情形会多出一项a % c,所以为了完成迭代,当b是奇数时,我们通过ans = (ans * a) % c; 来弥多出来的这一项
int ans = 1;
a = a % c;
while(b>0)
{
      if(b % 2 == 1)ans = (ans * a) % c;   //奇数,补一项
      b = b/2;
      a = (a * a) % c;
}
cout<<ans<<endl;

这时的时间复杂度是O(logb)

就是完整的快速幂算法!

#include<iostream>
#include<cstdio>
using namespace std;
long long int a,b,c;
long long int ans=1,bent;
int main()
{
    scanf("%d%d%d",&a,&b,&c);
    bent=a;
    while(b)
    {
        if(b&1)
        ans=(ans*bent)%c;
        bent=(bent*bent)%c;
        b/=2;
    }
    printf("%d",ans);
    return 0;
}

看一道题吧。。这个是裸的快速幂(点击收获RP)先把板子打会吧。。

(RP++!)

 

 

 

 
 

 

 

posted @ 2019-09-22 20:07  Kyoko_Yosa  阅读(285)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end