ACM数论模板

取模运算

         编程竞赛有相当一部分题目的数据结果过于庞大,往往需要对结果取模。例如(a*b) % p,若a*b的结果存储不了,再去取模,结果显然不对,为了防止溢出,可以分别对a取模,b取模,再求积取模。

   取模运算公式:
         加法:(a + b) % p = (a%p + b%p) % p
         减法:(a - b) % p = ((a%p - b%p) + p) % p
         乘法:(a * b) % p = (a%p)*(b%p) % p
         幂运算:a ^ b % p = ((a % p)^b) % p
         除法:(a / b) % p  = (a%p * inv%p) % p

    注意:(a / b) % p不能直接 / b 来求,需要找到一个数 inv 使得  inv * b % p= 1 。 我们称inv是逆元,下面介绍求逆元的方法。

         由于在竞赛中,通常让我们把结果 % 1e9+7 ,所以推荐使用方法一。当然如果a刚好是b的倍数可以直接计算。

/*==================================================*\ 
 | 方法一 适用范围:只要存在逆元即可求
 | 求b在mod下的逆元,不存在逆元返回-1 
 | exgcd为扩展欧几里得算法后面会讲到
\*==================================================*/
ll getInv(int b) {
    ll x, y;
    ll d = exgcd(b, mod, x, y);
    return d==1 ? (x+mod)%mod : -1;
}

/*==================================================*\ 
 | 方法二 适用范围:mod为素数?
 | 根据费马小定理,(a/b)%mod = a*pow_mod(b,mod-2)%mod
 | pow_mod为快速幂函数后面会讲到
\*==================================================*/
ll getInv(ll b) {
    return pow_mod(b, mod-2) % mod;
}

/*==================================================*\ 
 | 方法三 适用范围:mod较小时
\*==================================================*/
ll getInv(ll b) {
    if(b==1) return 1;
    return (mod-mod/b) * getInv(mod%b) % mod;
}

/*==================================================*\ 
 | 逆元打表:线性求逆元,可得到每个数对应的逆元
 | 求逆元定义变量时都使用longlong类型
\*==================================================*/
const int N = 100005;
ll inv[N];
void inv_init() {
    inv[0] = inv[1] = 1;
    for (int i = 2; i < N; i++) {
        inv[i] = ((mod - mod/ i) * inv[mod % i]) % mod;
    }
}

最大公约数(GCD)

/*==================================================*\
  | GCD 最大公约数 
\*==================================================*/ 
int gcd(int x, int y){  
    if (!x || !y) return x > y ? x : y;  
    for (int t; t = x % y; x = y, y = t);  
    return y; 
} 

/*==================================================*\
  | 快速 GCD 
\*============================================&
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值