快速幂取模

快速幂取模

1056.A ^ B Problem

          

Description

给定三个数A, B, K, 求 A的B次方除以K的余数 。

Input

输入只有一行,为三个正整数A(1 <= A <= 2000000000), B(1 <= B <= 2000000000), K(1 <= K <= 10000)。

Output

一个整数,(A ^ B) % K 的值。

Sample Input

11 100 7

Sample Output

4

直接做

int ans = 1;

for(int i = 1;i<=b;i++)

{

ans = ans * a;

}

ans = ans % c;

这个算法的时间复杂度很大,a和b过大,很容易就会溢,找到以下公式

上面公式为下面公式的引理,即积的取余等于取余的积的取余。

 

快速幂算法

 

 

1.如果b是偶数,我们可以记k = a2 mod c,那么求(k)b/2 mod c就可以了。

2.如果b是奇数,我们也可以记k = a2 mod c,那么求((k)b/2 mod c × a ) mod c =((k)b/2 mod c * a) mod c 就可以了。

 

代码如下:

 

#include <cstdio>
#include <cstdlib>
#include <algorithm>

int main()
{
    int ans = 1;
    int a,b,c,k;

    scanf("%d%d%d",&a,&b,&c);

    a = a % c;

    if(b%2==1)

        ans = (ans * a) % c ;

    k = (a*a) % c;

    for(int i = 1; i<=b/2; i++)

    {

        ans = (ans * k) % c;

    }
    ans = ans % c;

    printf("%d\n",ans);

    return 0;
}

 


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

  •   假设我们要求a^b,那么其实b是可以拆成二进制的,该二进制数第i位的权为2^(i-1),例如当b==11时

                         a^11=a^(2^0+2^1+2^3) 
      11的二进制是1011,11 = 2³×1 + 2²×0 + 2¹×1 + 2º×1,因此,我们将a¹¹转化为算 a^(2^0)*a^(2^1)*a^(2^3) ,看出来快的多了吧原来算11次,现在算三次,但是这三项貌似不好求的样子….不急,下面会有详细解释。    
      由于是二进制,很自然地想到用位运算这个强大的工具: & 和 >>    
      &运算通常用于二进制取位操作,例如一个数 & 1 的结果就是取二进制的最末位。还可以判断奇偶x&1==0为偶,x&1==1为奇。   
      >>运算比较单纯,二进制去掉最后一位。 
      现在已上边的式子为例:

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


    base*=base;
    • 1

    这个是完全为了达到累乘的效果,例如上题,这个就相当于把a^11变成了a^(2^0),a^(2^1),a^(2^3); 
    另一种详细的解释: 
    其中要理解base*=base这一步,base*base==base^2,下一步再乘,就是base^2*base^2==base^4,然后同理 base^4*base4=base^8,,,,,see?是不是做到了base–>base^2–>base^4–>base^8–>base^16–>base^32…….指数正是 2^i 啊

    山东省赛
  • #include <iostream>
    using namespace std;
    const int d=1000000000+7;
    long long poww(long a,long b)
    {
        long long ans=1,base=a;
        while(b!=0)
        {
            if(b&1)
                ans=(ans*base)%d;
            base=(base*base)%d;
            b>>=1;
        }
        return ans;
    }
    int main()
    {
        long long n,m;
        cin>>n>>m;
        long long sum = 0;
        for(int i=1;i<=n;i++)
            sum = (sum+poww(i,m))%d;
        cout<<sum<<endl;
        return 0;
    }

SDUT 3899

#include <iostream>
using namespace std;
const int d=1000000000+7;
long long poww(long a,long b)
{
    long long ans=1,base=a;
    while(b!=0)
    {
        if(b&1)
            ans=(ans*base)%d;
        base=(base*base)%d;
        b>>=1;
    }
    return ans;
}
int main()
{
    long long n,m;
    cin>>n>>m;
    long long sum = 0;
    for(int i=1;i<=n;i++)
        sum = (sum+poww(i,m))%d;
    cout<<sum<<endl;
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值