acm新手小白必看系列之(7)——快速幂取模精讲及例题

acm新手小白必看系列之(7)——快速幂取模精讲及例题

  • 性质1:(a+b)%m=(a%m+b%m)%m
  • 性质2:(ab)%m=(a%mb%m)%m

给你一个数a,让你求其b次连乘后的结果
当b很小时,一般的循环算法可以解决这个问题(O(B)),但是当b较大时呢
要知道1e18以上,就会long long int 也可能会溢出
而在数论方面这些数又该如何表示?怎样存储?
所以在此我们定义一个模数mod来代替输出即

a^b=k1*mod+t即t=a^b%mod

对于这样的问题,我们将采用分治的思想来解决这种问题。(啥叫分治呢,这个一时半会说不完,一会简单来说)

a^b=((a^(b/n))^n)*a^(b%n)

当n=2时

a^b=(a^(b/2))*(a^(b/2))*a^(b%2)
(a^b)%mod=(a^(b/2))%mod*(a^(b/2))%mod*a^(b%2)%mod

而后对a^(b/2)进行相同的操作
下面解释一下分治是什么
五大算法之一____分治
分治法是一种很重要的算法。字面上的解释是“分而治之”,就是把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题……直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并。这个技巧是很多高效算法的基础,如排序算法(快速排序,归并排序),傅立叶变换(快速傅立叶变换)……此法需要大量的练习才能体会。

  • 递归
typedef long long ll;//typedef 类型 新名字;就可以把long long简化成ll来写了
ll quickmod(ll a,ll b,ll c)
{
    if(b==1)
        return a;
    if(!(b&1))
    {
        ll t=quickmod(a,b/2,c);
        return t*t%c;
    }
    else
    {
        ll t=quickmod(a,b/2,c);
        return t*t%c*a%c;
    }
}


  • 非递归
typedef long long ll;//typedef 类型 新名字;

ll quickmod(ll a,ll b,ll c)
{
    int ret=1;
    while(b)
    {
        if(b&1)
            ret=ret*a%c;
        a=a*a%c;
        b/=2;
    }
    return ret;
}

相信大家看了这么多还是不太明白,下面来个咱来个“明白题”(白给)
1.白给的快速幂取模
给定A,B,C,计算A^B%C,这里A^B代表A的B次方
Input
输入数据有多组,每组数据一行,有3个正整数分别为A,B和C,1<=A,B,C<=1000000000
Output
输出A^B%C的值
Sample Input
2 3 5
8 2 10
Sample Output
3
4

#include <bits/stdc++.h>//套模板吧,但是要理解其核心
using namespace std;
long long mode(long long a,long long b,long long mod)
{
    long long ans=1;
    while(b)
    {
        if(b%2==1)
        {b--;ans=ans*a%mod;}
        a=a*a%mod;
        b=b/2;
    }
    return ans;
}
int main()
{
    long long a,b,c;
    while(cin>>a>>b>>c)
    printf("%lld\n",mode(a,b,c));
    return 0;
}

2.小蓝数学题
xaiolan很喜欢做各种高深莫测的数学题,一天,她在书上看到了这么一道题。a[1]=6,a[2]=18;a[n]=2*a[n-1]+3*a[n-2](n>=3),对于给出的某个数字n,求a[n]。xiaolan一想这道题太简单了,可是看到n的范围是(n<=1e18),对于这么大范围的数,xiaolan不知道该怎么做了,聪明的你,快来帮帮库xiaolanjiejie解决这个问题吧。(由于答案可能很大,请将答案对1e9+7(即1000000007)取模)。

Input

一个整数n(1<=n<=1e18)

Output

a[n]对1e9+7取模后的答案

Sample Input

5

Sample Output

486
关键点推出a[n]=6乘3的(n-1)次幂

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n,mod=1e9+7;
ll quickmod(ll a,ll b)
{
    ll s=1;
    while(b)
    {
        if(b&1)s=s*a%mod;
        a=a*a%mod;
        b=b/2;
    }
    return s;
}
int main()
{
    ios::sync_with_stdio(false);//取消同步,详请看往期
    cin>>n;
    printf("%lld\n",6*quickmod(3,n-1)%mod);//注意算出3的n-1次幂,再乘以6之后一定还要再取模!
    return 0;
}

我知道你还没懂,所以
已知 2^3 求 2^6, 不就是 2^3 * 2^3
快速幂就是这个原理。
遇到奇数怎么办?
那不就是 2 * 2 ^ 4 这不就成了嘛~~
所以这就是快速幂的基本思路求a ^ b
1)当b是奇数时,那么有 a^b = a * a^(b-1)
2)当b是偶数时,那么有 a^b = a^(b/2) * a^(b/2)
举个例子?2 ^10

   2^10 = 2^5 * 2^5
    2^5 = 2 * 2^4
    2^2 = 2^1 * 2^1
    2^1 = 2 * 2^0

3.这个看看他的写的,详细(手动滑稽)但是他没有我讲解的系统啦~

练习题(应铁粉要求特增加习题栏~代码会在下一节公布)

4.越狱
Description
  监狱有连续编号为1…N的N个房间,每个房间关押一个犯人,有M种宗教,每个犯人可能信仰其中一种。如果
相邻房间的犯人的宗教相同,就可能发生越狱,求有多少种状态可能发生越狱
Input

输入两个整数M,N.1<=M<=10^8,1<=N<=10^12

Output
  可能越狱的状态数,模100003取余
Sample Input
2 3
Sample Output
6
HINT
  6种状态为(000)(001)(011)(100)(110)(111)

  • 下节提示
  • acm新手小白必看系列之(8)——二分法精讲及例题
发布了8 篇原创文章 · 获赞 73 · 访问量 4772

猜你喜欢

转载自blog.csdn.net/qq_45899321/article/details/103846484