[Some Tricks] 自动取模类

1. 源码

typedef long long i64;
typedef unsigned long long u64;
typedef __int128 i128;
const i128 o = 1;
template < i64 mod, i64 invpow = mod - 2 >
struct Modular {
  u64 M = (o << 64) / mod;
  i64 query (i64 x) {
    u64 x_ = 1ull * x;
    u64 q = 1ull * (((i128) (M) * (i128) (x_)) >> 64);
    u64 r = x_ - q * (1ull * mod);
    return r >= mod ? 1ll * r - mod : 1ll * r;
  }
  i64 x;
  i64 norm (i64 v) {v = (v < 0) ? (v + mod) : v, v = (v >= mod) ? (v - mod) : v, v = (v >= mod) ? query (v) : v; return v;}
  i64 pow_mod (i64 a, i64 b) {i64 res = 1; for (i64 i = b; i > 0; i >>= 1, a = norm (a * a)) res = (i & 1) ? norm (res * a) : res; return res;}
  void init (i64 rhs) {x = norm (rhs); return ;}
  i64 val () {return norm (x);}
  void neg () {x = norm (mod - x); return ;}
  Modular inv () {assert (x != 0); auto res = *this; res.x = pow_mod (res.x, invpow); return res;}
  void add () {x = norm (x + 1); return ;}
  void sub () {x = norm (x - 1); return ;}
  Modular &operator += (Modular rhs) & {x = norm (x + rhs.x); return *this;}
  Modular &operator -= (Modular rhs) & {x = norm (x - rhs.x); return *this;}
  Modular &operator *= (Modular rhs) & {x = norm (x * rhs.x); return *this;}
  Modular &operator /= (Modular rhs) & {*this *= rhs.inv (); return *this;}
  Modular &operator += (i64 rhs) & {rhs = norm (rhs), x = norm (x + rhs); return *this;}
  Modular &operator -= (i64 rhs) & {rhs = norm (rhs), x = norm (x - rhs); return *this;}
  Modular &operator *= (i64 rhs) & {rhs = norm (rhs), x = norm (x * rhs); return *this;}
  Modular &operator /= (i64 rhs) & {rhs = norm (rhs), *this *= pow_mod (rhs, invpow); return *this;}  
  Modular &operator ^= (i64 ind) & {x = pow_mod (x, ind), x = (ind < 0) ? pow_mod (x, invpow) : x; return *this;}
  friend Modular operator + (Modular lhs, Modular rhs) {auto res = lhs; res += rhs; return res;}
  friend Modular operator - (Modular lhs, Modular rhs) {auto res = lhs; res -= rhs; return res;}
  friend Modular operator * (Modular lhs, Modular rhs) {auto res = lhs; res *= rhs; return res;}
  friend Modular operator / (Modular lhs, Modular rhs) {auto res = lhs; res /= rhs; return res;}
  friend Modular operator ^ (Modular lhs, i64 ind) {auto res = lhs; res ^= ind; return res;}
  friend bool operator == (Modular lhs, Modular rhs) {return (lhs.val () == rhs.val ());}
  friend bool operator != (Modular lhs, Modular rhs) {return (lhs.val () != rhs.val ());}
  friend bool operator > (Modular lhs, Modular rhs) {return (lhs.val () > rhs.val ());}
  friend bool operator < (Modular lhs, Modular rhs) {return (lhs.val () < rhs.val ());}
  friend bool operator >= (Modular lhs, Modular rhs) {return (lhs.val () >= rhs.val ());}
  friend bool operator <= (Modular lhs, Modular rhs) {return (lhs.val () <= rhs.val ());} 
};
template < i64 mod, i64 invpow = mod - 2 >
Modular < mod, invpow > addmod (Modular < mod, invpow > lhs, i64 rhs) {
  rhs = lhs.norm (rhs);
  lhs.x = lhs.norm (lhs.x + rhs);
  return lhs;
}
template < i64 mod, i64 invpow = mod - 2 >
Modular < mod, invpow > submod (Modular < mod, invpow > lhs, i64 rhs) {
  rhs = lhs.norm (rhs);
  lhs.x = lhs.norm (lhs.x - rhs);
  return lhs;
}
template < i64 mod, i64 invpow = mod - 2 >
Modular < mod, invpow > mulmod (Modular < mod, invpow > lhs, i64 rhs) {
  rhs = lhs.norm (rhs);
  lhs.x = lhs.norm (lhs.x * rhs);
  return lhs;
}
template < i64 mod, i64 invpow = mod - 2 >
Modular < mod, invpow > divmod (Modular < mod, invpow > lhs, i64 rhs) {
  rhs = lhs.norm (rhs);
  Modular < mod, invpow > it;
  it.init (rhs);
  lhs.x = lhs.norm (lhs.x * it.val ());
  return lhs;
}

2. 使用说明

2.1 定义一个 Modular

2.1.1 定义过程

定义一个自动取模类可以写:Modular < mod, invpow >,其中 \(mod\)模数\(invpow = \varphi(mod) - 1\)

可以证明对于任意的 \(x\) 满足 \(\gcd(x, mod) = 1\),有:

\[x^{-1} \bmod mod = x^{invpow} \bmod mod \]

根据欧拉定理,有对于任意的 \(x\) 满足 \(\gcd(x, mod) = 1\),有:

\[x^{\varphi(mod)} \bmod mod = 1 \]

等式两边各乘 \(x^{-1}\),有:

\[x^{-1} \bmod mod = x^{\varphi(mod) - 1}\bmod mod \]

得证。

因为在 OI 中模数大部分是质数,进而有 \(\varphi(mod) = mod - 1\),即 \(invpow = mod - 2\),所以我们 默认 \(invpow = mod - 2\)。(即如果写 Modular < mod > 的话 \(invpow\) 就默认为 \(mod - 2\)

2.1.2 时间复杂度

\(O(1)\)

2.2 query 函数

注:此函数在一般情况下不直接使用

此函数的实现基于快速取模。

2.2.1 用途

query 函数可用于卡常数,且有 \(\text{query}(x) = x \bmod mod\)

2.2.2 预处理

定义 \(M = \left\lfloor \dfrac{2^{64}}{mod} \right\rfloor\)

2.2.3 函数原理

对于每一个 \(x\),我们要返回 \(x \bmod mod\)

\(q = \left\lfloor \dfrac{M \times x}{2^{64}} \right\rfloor, r = x - q \times mod\)。则有:

\[x \bmod mod = \begin{cases}r, r < mod\\ r - mod, r \geq mod\end{cases} \]

考虑证明:

考虑带入 \(M\),那么有:

\[q = \left\lfloor \dfrac{\left\lfloor \frac{2^{64}}{mod} \right\rfloor \times x}{2^{64}} \right\rfloor \]

\[q = \left\lfloor \dfrac{\frac{2^{64}-2^{64} \bmod mod}{mod} \times x}{2^{64}} \right\rfloor \]

\[q = \left\lfloor \dfrac{(2^{64}-2^{64} \bmod mod) \times x}{2^{64} \times mod} \right\rfloor \]

带入 \(q\),有:

\[r = x - \left\lfloor \dfrac{(2^{64}-2^{64} \bmod mod) \times x}{2^{64} \times mod} \right\rfloor \times mod \]

\[r = x - \left\lfloor \dfrac{2^{64} \times x}{2^{64} \times mod} - \dfrac{(2^{64} \bmod mod) \times x}{2^{64} \times mod} \right\rfloor \times mod \]

\[r = x - \left\lfloor \dfrac{x}{mod} - \dfrac{(2^{64} \bmod mod) \times x}{2^{64} \times mod} \right\rfloor \times mod \]

因为 \(r\)\(x\) 减去一个 \(mod\) 的倍数,所以 \(r \bmod mod = x \bmod mod\)

因为 \(\left\lfloor \dfrac{x}{mod} - \dfrac{(2^{64} \bmod mod) \times x}{2^{64} \times mod} \right\rfloor \leq \left\lfloor \dfrac{x}{mod} \right\rfloor\),所以 \(r \geq x - \left\lfloor \dfrac{x}{mod} \right\rfloor \times mod\),即 \(r \geq x \bmod mod\)

所以 \(r\) 一定可以写成 \(x \bmod mod + \epsilon \times mod\) 的形式。

显然 \(\epsilon\) 一定是一个整数,且下界为 \(0\)

下求 \(\epsilon\) 的上界。

因为原函数中的 xlong long 类型的,所以有 \(x < 2^{64}\)。显然有 \(2^{64} \bmod mod < mod\),所以有:

\[\dfrac{(2^{64} \bmod mod) \times x}{2^{64} \times mod} < 1 \]

所以有 \(\left\lfloor \dfrac{x}{mod} \right\rfloor - 1 \leq \left\lfloor \dfrac{x}{mod} - \dfrac{(2^{64} \bmod mod) \times x}{2^{64} \times mod} \right\rfloor \leq \left\lfloor \dfrac{x}{mod} \right\rfloor\)

\(\epsilon\) 的上界为 \(1\),所以有 \(0 \leq \epsilon \leq 1\)

所以得到的 \(r\) 至多减去 \(\symbfit{1}\)\(\symbfit{mod}\) 即可得到正确的值

得证。

2.2.4 时间复杂度

\(O(1)\)

2.3 norm 函数

2.3.1 用途

调用 \(\text{norm}(x)\),可以得到 \(x \bmod mod\)

2.3.2 函数原理

如果 \(x\) 减去 \(1\)\(mod\) 可以满足要求就直接减。如果 \(x < 0\) 就加上一次 \(mod\)。否则调用 query 函数。

2.3.3 正确性

此函数在 Modular 类其他运算及时取模的前提下有正确性。

2.3.4 时间复杂度

\(O(1)\)

2.4 pow_mod 函数

2.4.1 用途

调用 \(\text{pow_mod}(a, b)\) 可以得到 \(a^b \bmod mod\) 的值。

2.4.2 函数原理

与快速幂原理相同。如果不知道快速幂的原理请回普及组重学。

2.4.3 时间复杂度

\(O(\log b)\)

2.5 init 函数

2.5.1 用途

调用 \(\text{init}(rhs)\) 可以将此类对应的值赋值为 \(rhs\)

2.5.2 时间复杂度

\(O(1)\)

2.6 val 函数

2.6.1 用途

调用 \(\text{val}()\) 可以返回此类对应的值(long long)。

2.6.2 时间复杂度

\(O(1)\)

2.7 neg 函数

2.7.1 用途

调用 \(\text{neg}()\) 可以将此类对应的值乘上 \(\symbfit{-1}\)

2.7.2 时间复杂度

\(O(1)\)

2.8 inv 函数

2.8.1 用途

设此类对应的值为 \(x\),则调用 \(\text{inv}()\) 可以返回 \(x^{-1}\)

2.8.2 时间复杂度

\(O(\log mod)\)

2.9 add 函数

2.9.1 用途

调用 \(\text{add}()\) 可以将类对应的值加上 \(1\)

2.9.2 时间复杂度

\(O(1)\)

2.10 sub 函数

2.10.1 用途

调用 \(\text{sub}()\) 可以将类对应的值加上 \(-1\)

2.10.2 时间复杂度

\(O(1)\)

2.11 Modular 类运算符号

2.11.1 内容

  • Modular += Modular:将两个 Modular 类相加,返回的 Modular 类对应的值为两个 Modular 类对应的值的和

  • Modular -= Modular:将两个 Modular 类相减,返回的 Modular 类对应的值为两个 Modular 类对应的值的差

  • Modular *= Modular:将两个 Modular 类相乘,返回的 Modular 类对应的值为两个 Modular 类对应的值的积

  • Modular /= Modular:将两个 Modular 类相除,返回的 Modular 类对应的值为两个 Modular 类对应的值的商

  • Modular ^= long long:返回对应的值为 \(\text{Modular}^{\text{long long}}\)Modular 类。

上面的运算符可以使用为 Modular + Modular, Modular - Modular, Modular * Modular, Modular / Modular, Modular ^ long long

2.11.2 时间复杂度

除求商或求幂运算的时间复杂度为 \(O(\log mod)\),其他运算均为 \(O(1)\)

2.12 Modular 类比较符号

2.12.1 内容

  • Modular == Modular:若第一个 Modular 类对应的值与第二个 Modular 类对应的值相等则返回 true,否则返回 false

  • Modular != Modular:若第一个 Modular 类对应的值与第二个 Modular 类对应的值不相等则返回 true,否则返回 false

  • Modular > Modular:若第一个 Modular 类对应的值大于第二个 Modular 类对应的值则返回 true,否则返回 false

  • Modular < Modular:若第一个 Modular 类对应的值小于第二个 Modular 类对应的值则返回 true,否则返回 false

  • Modular >= Modular:若第一个 Modular 类对应的值大于等于第二个 Modular 类对应的值则返回 true,否则返回 false

  • Modular <= Modular:若第一个 Modular 类对应的值小于等于第二个 Modular 类对应的值则返回 true,否则返回 false

2.12.2 时间复杂度

\(O(1)\)

2.13 addmod, submod, mulmod, divmod 函数

2.13.1 用途

这四个函数是用来将 Modular 类和 long long 进行运算的。

2.13.2 时间复杂度

  • addmod, submod, mulmod 函数都是 \(O(1)\) 的。
  • divmod 函数是 \(O(\log mod)\) 的。
posted @ 2024-02-18 15:08  CountingGroup  阅读(78)  评论(0编辑  收藏  举报