温习(一)——快速幂

63 篇文章 0 订阅
30 篇文章 0 订阅

快速幂,是属于分治的一类习题,其思想如同分治,就是分而治之,所以看到题目的大数据,就要思考如何将大问题拆分成若干个相同解法的小问题,所以推导公式:

  • (a^b)%k=a^(b/2)*a^(b/2)*a(b%2)
    所以,这就是这道题目的一般性推导公式,这时程序就写出来了
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
long long b,p,k;
long long work(int n){
    if(n==0)return 1;
    long long tmp=work(n/2)%k;
    tmp=(tmp*tmp)%k;
    if(n&1)tmp=(tmp*b)%k;
    return tmp%k;
}
int main(){
    scanf("%lld%lld%lld",&b,&p,&k);
    printf("%lld^%lld mod %lld=",b,p,k);
    b%=k;
    printf("%d\n",work(p));
    return 0;
}

当然这并不是最快的快速幂,你还可以采用更高效的方法,如二进制:
在二进制中,有一个神奇的运算叫做按位与,它的意思是如果在对其的情况下,两位都是1,取出来的结果就是1,否则就是0。在这个的基础上,我们在推导n&1的情况(n为任意整数):
若n为11,则(n)10=(11)10=(1011)2 00001011
所以,11&1———————————–00000001
00000001
这个方法可以用来判断这个数的奇偶(这不是重点),但真正神奇的是下面这一个式子:
(a^11)%k=(a^8)%k*(a^2)%k*(a^1)%k
这也是分治的思想,运用二进制,使时间复杂度缩短为log2(a)。

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
long long b,p,k;
int main(){
    int i,j,n,m;
    scanf("%lld%lld%lld",&b,&p,&k); 
    printf("%lld^%lld mod %lld=",b,p,k);
    b%=k;
    long long tmp=b,ans=1;
    while(p!=0){
        if(p&1){
            ans=(ans*tmp)%k;
        }
        tmp=(tmp*tmp)%k;
        p>>=1;
    }
    printf("%lld\n",ans%k);
    return 0;
}

这里有一道快速幂的题目:转圈游戏,这道题目是一道NOIP提高组的题目,总体来说就是要推导公式,这一题给大家思考,提供标程:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
int work(int b,int n,int k){
    if(n==0)return 1;
    int tmp=work(b,n/2,k)%k;
    tmp=(tmp*tmp)%k;
    if(n&1)tmp=(tmp*b)%k;
    return tmp%k;
}
int main(){
    int i,j,k,n,m,x;
    scanf("%d%d%d%d",&n,&m,&k,&x);
    int b=10;b%=n;
    int tmp=work(b,k,n);
    tmp=(tmp*m)%n;
    tmp+=x;tmp%=n;
    printf("%d\n",tmp);
    return 0;
}
//公式:(x+m*10^k)%n 

这里是改进版的(二进制实现):

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
int bb,pp;
int main(){
    int i,j,n,m,k,x;
    scanf("%d%d%d%d",&n,&m,&k,&x);
    bb=10;bb%=n;pp=k;
    int ans=1;
    while(pp!=0){
        if(pp&1){
            ans=(ans*bb)%n;
        }
        bb=(bb*bb)%n;
        pp>>=1;
    }
    ans=(ans*m)%n;
    ans+=x;ans%=n;
    printf("%d\n",ans);
    return 0;
}
//(x+m*10^k)%n
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值