取模运算
编程竞赛有相当一部分题目的数据结果过于庞大,往往需要对结果取模。例如(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
\*============================================&