模运算(带例题详解)

模运算

基本规则

模运算的基本规则与四则运算基本一致:

  • (a + b) % p = (a % p + b % p) % p

  • (a – b) % p = (a % p – b % p) % p

  • (a * b) % p = (a % p * b % p) % p

  • (a^b) % p = ((a % p)^b) % p

这里没有除法的模运算是因为除法取模较为特殊。

除法取模

除法取模首先需要知道小费马定理。

  • 小费马定理

    • 乘法逆元

      定义: 对于整数a,p且gcd(a,p)==1,则一定存在唯一一个b满足a*b≡1(mod p)。

    • 若p是质数,且p与a互质,有等式:

( 1 ) . a p − 1 % p = = 1 % p ( 2 ) . a p − 2 % p = = 1 a % p 2 中 的 右 式 其 实 就 是 b 的 乘 法 逆 元 。 (1).a^{p-1}\%p == 1\%p\\ (2).a^{p-2}\%p==\frac{1}{a}\%p\\ 2中的右式其实就是b的乘法逆元。 (1).ap1%p==1%p(2).ap2%p==a1%p2b
转化后:
两 边 同 除 以 a ⇒ b a % p = = b ∗ a p − 2 % p 此 时 我 们 得 到 所 求 的 b a 的 模 就 是 b ∗ a p − 2 的 模 两边同除以a\Rightarrow\frac{b}{a}\%p == b*a^{p-2}\%p\\ 此时我们得到所求的\frac{b}{a}的模就是b*a^{p-2}的模 aab%p==bap2%pabbap2
此时我们就将分数求模问题转化为了乘法求模,同时也是求b的乘法逆元问题。

同时若p值较大,则有要用到高次幂求模。

代码

//分数求模
const long long mod=1e9+7;
long long power_mod(long long a, long long b, long long mod)
{
    long long ans = 1;
    while (b)
    {
        if (b & 1) ans = ans * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return ans;
}

int main()
{
    long long a,b;
    int n;
    while(scanf("%lld%lld", &a, &b)==2)//表示b/a
    printf("%lld\n", (power_mod(a,mod-2,mod))*b%mod);
    return 0;
}

  • 高次幂求模

    传统方法当然就是依次相乘,显然当p很大时,耗时也会很大。

    也就是说问题是出在p值太大的情况上,此时我们想到能否将p值缩小,将高次幂指数运算转化为多个低次幂的指数运算乘积。

    通过位运算的方法可以很好地解决这个问题:
    假 设 我 们 求 2 10 这 个 数 的 值 , 我 们 可 以 将 10 的 二 进 制 数 表 示 出 来 : 1010 。 此 时 我 们 可 以 根 据 位 上 为 1 的 数 的 权 值 对 这 个 指 数 进 行 拆 分 : 2 10 = 2 10 ∗ 2 1000 = 2 2 ∗ 2 8 显 然 上 面 的 等 式 成 立 , 而 此 时 我 们 只 需 遍 历 最 多 4 次 即 可 ( 指 数 的 二 进 制 位 数 ) 。 假设我们求2^{10}这个数的值,我们可以将10的二进制数表示出来:1010。\\ 此时我们可以根据位上为1的数的权值对这个指数进行拆分:2^{10}=2^{10}*2^{1000}=2^2*2^8\\ 显然上面的等式成立,而此时我们只需遍历最多4次即可(指数的二进制位数)。 2101010101210=21021000=22284
    代码:

    int pow_mod(int a,int b,int c){
        int ans = 1;
        int base = a%c;
        while(b){
            if(b & 1) ans = (ans*base)%c;
            base = (base*base)%c;
            b >>= 1;
        }
        return ans;
    }
    
例题

链接:https://ac.nowcoder.com/acm/contest/275/A
来源:牛客网

题目描述

你在一栋楼房下面,楼房一共有n层,第i层每秒有pi的概率会扔下一个东西并砸到你
求第一秒内你被砸到的概率

输入描述:
第一行一个整数n
之后有n行,第i+1行有两个整数ai,bi,表示pi=aibip_i=\frac{a_i}{b_i}pi=biai
输出描述:
设答案为pq\frac{p}{q}qp,你只需要找到一个最小的非负整数T,使得T≡p×q−1( mod 1000000007)T \equiv p \times q^{-1} (\bmod 1000000007)T≡p×q−1(mod1000000007)
输出这个T就行了

示例1

输入
2
1 2
1 2
输出
750000006

代码

#include <iostream>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

using namespace std;

const long long mod=1e9+7;
long long power_mod(long long a, long long b, long long mod)
{
    long long ans = 1;
    while (b)
    {
        if (b & 1) ans = ans * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return ans%mod;
}

int main()
{
    long long a,b;
    int n;
    long long ans=1;
    scanf("%d",&n);
    while(n--){
        scanf("%lld%lld", &a, &b);
        ans=(ans*(b-a)% mod)*(power_mod(b,mod-2,mod))%mod;//不可能的概率
    }
    printf("%lld\n", (1+mod-ans)%mod);
    return 0;
}

  • 14
    点赞
  • 78
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值