介绍

莫号模板库Mohao's Template Folder, MTF)是一个由我个人创建个人使用欢迎贡献的算法竞赛模板库。

主要的设计灵感来自于 AtCoder Library 以及 WIDA 的 XCPC 模板库

目前正在施工中……

约定

  1. C++ 版本:
    1. 最低支持版本:c++17
    2. 默认版本:gnu++23
  2. 格式:
    1. 缩进宽度为 4 个空格;
    2. 大括号换行;
    3. 一元运算符不空格,其他运算符空一格;
  3. 命名:
    1. 遵循 GoLang 的命名规范;
  4. 其他:
    1. 使用邻接表存图;
    2. 使用 0-indexed 的左闭右闭区间;
    3. 使用解绑同步流的 std::cinstd::cout 输入输出;
    4. 使用 using namespace std;,非必要不使用 #define int long long
    5. 使用局部变量而非全局变量,局部 lambda 而非全局函数;
    6. 使用 emplace 而非 push,使用 contains 而非 count
    7. 若键值较小,使用 vector 而非 map;不要求顺序的情况下可以用 unordered_ 系列,但在 Codeforces 上记得使用随机模数;

初始代码

#include <bits/stdc++.h>
using namespace std;

signed main() {

}
auto FAST_IO = cin.tie(nullptr)->sync_with_stdio(false);

随机哈希

使用的随机算法

struct custom_hash {
    static u64 splitmix64(u64 x) {
        x += 0x9e3779b97f4a7c15;
        x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9;
        x = (x ^ (x >> 27)) * 0x94d049bb133111eb;
        return x ^ (x >> 31);
    }

    u64 operator()(u64 x) const {
        static const u64 SEED =
            std::chrono::high_resolution_clock::now().time_since_epoch().count();
        return splitmix64(x + SEED);
    }
};

二分答案

    int l, r, ans;
    bool check(int);
    while (l <= r) {
        int mid = l + (r - l) / 2;
        if (check(mid)) {
            ans = mid;
            // l 和 r 取决于方向
            l = mid + 1;
        } else {
            r = mid - 1;
        }
    }

Lambda 递归

    // C++23
    auto dfs1 = [&](this auto &&self, int x, int p) -> void {
        for (auto y : adj[x]) {
            if (y == p)
                continue;
            self(y, x);
        }
    };
    // C++17
    std::function<void(int, int)> dfs2 = [&](int x, int p) {
        for (auto y : adj[x]) {
            if (y == p)
                continue;
            dfs2(y, x);
        }
    };

线性筛 (Sieve)

inline void Sieve(int n, auto &&pri, auto &&mp) {
    mp.resize(n + 1);
    for (int i = 2; i <= n; i++) {
        if (!mp[i])
            pri.emplace_back(i);
        for (int p : pri) {
            if (i * p > n)
                break;
            mp[i * p] = p;
            if (i % p == 0)
                break;
        }
    }
}

快速幂

inline int PowMod(i64 a, i64 b, int p) {
    i64 res = 1;
    while (b) {
        if (b & 1) res = res * a % p;
        b = b >> 1;
        a = a * a % p;
    }
    return res;
}

因数/约数分解

inline auto Factor(int n) {
    std::vector<std::pair<int, int>> res;
    for (int i = 2; i * i <= n; i++) {
        int cur = 0;
        while (n % i == 0) {
            cur++;
            n /= i;
        }
        if (cur != 0)
            res.emplace_back(i, cur);
    }
    if (n > 1)
        res.emplace_back(n, 1);
    return res;
}

inline auto Divisor(int n) {
    std::vector<int> res;
    for (int i = 2; i * i <= n; i++) {
        if (n % i == 0) {
            res.emplace_back(i);
            if (i * i != n)
                res.emplace_back(n / i);
        }
    }
    std::sort(res.begin(), res.end());
    return res;
}

扩展欧几里得 (ExGCD)

inline auto ExGCD(auto a, auto b, auto &x, auto &y) {
    if (b == 0) {
        x = 1, y = 0;
        return a;
    }
    auto d = exgcd(b, a % b, y, x);
    y -= a / b * x;
    return d;
}

并查集 (DSU)

struct DSU {
    std::vector<int> f, sz;

    DSU(int n) : f(n), sz(n, 1) { std::iota(f.begin(), f.end(), 0); }

    int Find(int x) {
        while (x != f[x])
            x = f[x] = f[f[x]];
        return x;
    }

    void Merge(int x, int y) {
        x = Find(x), y = Find(y);
        if (x == y)
            return;
        f[y] = x;
        sz[x] += sz[y];
    }
};

树状数组 (Fenwick)

template <typename T> struct Fenwick {
    int n;
    std::vector<T> a;

    Fenwick(int n) : n(n), a(n + 1) {}

    void Add(int x, T v) {
        for (; x <= n; x += x & -x)
            a[x] += v;
    }

    T sum(int x) {
        T res = {};
        for (; x; x -= x & -x)
            res += a[x];
        return res;
    }

    T Sum(int l, int r) { return sum(r) - sum(l - 1); }
};

线段树 (SegTree)

关于 S 的约束:

  • 存在满足封闭性结合律的 operator+ 运算;
  • 存在单位元 {} (具有默认构造函数,且满足和单位元运算后不改变原值)

关于初始数组 vector<T>

  • T \(\neq\) S,则必须保证 S 存在可由 T 构造的构造函数。
template <class S>
struct SegTree {

    int n;
    std::vector<S> tr;

    SegTree(int n, const S &e) {
        build(n, std::vector<S>(n, e));
    }

    SegTree(auto &&l, auto &&r) {
        build(r - l, l);
    }

    void build(int m, auto &&arr) {
        for (n = 1; n < m; n <<= 1);
        tr.resize(n << 1);
        for (int i = 0; i < m; i++)
            tr[i + n] = arr[i];
        for (int i = n - 1; i >= 1; i--)
            pull(i);
    }

    void pull(int k) {
        tr[k] = tr[k << 1] + tr[k << 1 | 1];
    }

    void Set(int p, const S &x) {
        p += n;
        tr[p] = x;
        for (p >>= 1; p; p >>= 1)
            pull(p);
    }

    S Get(int p) {
        return tr[p + n];
    }

    S Query(int l, int r) {
        l += n, r += n;
        S sml{}, smr{};
        while (l < r) {
            if (l & 1)
                sml = sml + tr[l++];
            if (r & 1)
                smr = tr[--r] + smr;
            l >>= 1;
            r >>= 1;
        }
        return sml + smr;
    }
};

懒标记线段树

关于 F 的约束:

  • 存在满足封闭性的 operator+= 运算;
  • 存在一个恒等映射 {}(默认构造函数);
  • 存在运算 operator== 判断是否和恒等映射相等;
    • 其实这里也不是非常必要的,但是可以提高性能;

关于 S 的约束:

  • 存在满足封闭性结合律的 operator+ 运算;
  • 存在单位元 {} (具有默认构造函数,且满足和单位元运算后不改变原值)
  • 存在 operator*= 运算满足将映射 F 应用于 S 返回一个 S,并且满足分配律。

关于初始数组 vector<T>

  • T \(\neq\) S,则必须保证 S 存在可由 T 构造的构造函数。
template<class S, class F>
struct LazySegTree {

    int n, h;
    std::vector<S> tr;
    std::vector<F> lz;

    LazySegTree(int n, const S &e) {
        build(n, std::vector<S>(n, e));
    }

    LazySegTree(auto &&l, auto &&r) {
        build(r - l, l);
    }

    void build(int m, auto &&arr) {
        for (n = 1; n < m; n <<= 1);
        h = __builtin_ctz(n);
        tr.resize(n << 1);
        lz.resize(n);
        for (int i = 0; i < m; i++)
            tr[i + n] = arr[i];
        for (int i = n - 1; i >= 1; i--)
            pull(i);
    }

    void apply(int k, const F &f) {
        tr[k] *= f;
        if (k < n)
            lz[k] += f;
    }

    void pull(int k) {
        tr[k] = tr[k << 1] + tr[k << 1 | 1];
    }

    void push(int k) {
        apply(k << 1, lz[k]);
        apply(k << 1 | 1, lz[k]);
        lz[k] = {};
    }

    void Set(int p, const S &x) {
        p += n;
        for (int i = h; i >= 1; i--) push(p >> i);
        tr[p] = x;
        for (int i = 1; i <= h; i++) pull(p >> i);
    }

    S Get(int p) {
        p += n;
        for (int i = h; i >= 1; i--) push(p >> i);
        return tr[p];
    }

    void Update(int l, int r, const F &f) {
        l += n, r += n;
        for (int i = h; i >= 1; i--) {
            if ((l & ((1 << i) - 1)) != 0) push(l >> i);
            if ((r & ((1 << i) - 1)) != 0) push((r - 1) >> i);
        }
        {
            int l_ = l, r_ = r;
            while (l < r) {
                if (l & 1) apply(l++, f);
                if (r & 1) apply(--r, f);
                l >>= 1;
                r >>= 1;
            }
            l = l_;
            r = r_;
        }
        for (int i = 1; i <= h; i++) {
            if ((l & ((1 << i) - 1)) != 0) pull(l >> i);
            if ((r & ((1 << i) - 1)) != 0) pull((r - 1) >> i);
        }
    }

    S Query(int l, int r) {
        l += n, r += n;
        for (int i = h; i >= 1; i--) {
            if ((l & ((1 << i) - 1)) != 0) push(l >> i);
            if ((r & ((1 << i) - 1)) != 0) push((r - 1) >> i);
        }
        S sml{}, smr{};
        while (l < r) {
            if (l & 1)
                sml = sml + tr[l++];
            if (r & 1)
                smr = tr[--r] + smr;
            l >>= 1;
            r >>= 1;
        }
        return sml + smr;
    }
};

取模类 (ModInt)

using u32 = unsigned;
using i64 = long long; using u64 = unsigned long long;

template <class T> constexpr T power(T a, u64 b, T res = 1) {
    for (; b != 0; b /= 2, a *= a) {
        if (b & 1) {
            res *= a;
        }
    }
    return res;
}

template <u32 P> constexpr u32 mulMod(u32 a, u32 b) { return u64(a) * b % P; }

template <u64 P> constexpr u64 mulMod(u64 a, u64 b) {
    u64 res = a * b - u64(1.L * a * b / P - 0.5L) * P;
    res %= P;
    return res;
}

constexpr i64 safeMod(i64 x, i64 m) {
    x %= m;
    if (x < 0) {
        x += m;
    }
    return x;
}

constexpr std::pair<i64, i64> invGcd(i64 a, i64 b) {
    a = safeMod(a, b);
    if (a == 0) {
        return {b, 0};
    }

    i64 s = b, t = a;
    i64 m0 = 0, m1 = 1;

    while (t) {
        i64 u = s / t;
        s -= t * u;
        m0 -= m1 * u;

        std::swap(s, t);
        std::swap(m0, m1);
    }

    if (m0 < 0) {
        m0 += b / s;
    }

    return {s, m0};
}

template <std::unsigned_integral U, U P> struct ModIntBase {
  public:
    constexpr ModIntBase() : x(0) {}
    template <std::unsigned_integral T> constexpr ModIntBase(T x_) : x(x_ % mod()) {}
    template <std::signed_integral T> constexpr ModIntBase(T x_) {
        using S = std::make_signed_t<U>;
        S v = x_ % S(mod());
        if (v < 0) {
            v += mod();
        }
        x = v;
    }

    constexpr static U mod() { return P; }

    constexpr U val() const { return x; }

    constexpr ModIntBase operator-() const {
        ModIntBase res;
        res.x = (x == 0 ? 0 : mod() - x);
        return res;
    }

    constexpr ModIntBase inv() const { return power(*this, mod() - 2); }

    constexpr ModIntBase &operator*=(const ModIntBase &rhs) & {
        x = mulMod<mod()>(x, rhs.val());
        return *this;
    }
    constexpr ModIntBase &operator+=(const ModIntBase &rhs) & {
        x += rhs.val();
        if (x >= mod()) {
            x -= mod();
        }
        return *this;
    }
    constexpr ModIntBase &operator-=(const ModIntBase &rhs) & {
        x -= rhs.val();
        if (x >= mod()) {
            x += mod();
        }
        return *this;
    }
    constexpr ModIntBase &operator/=(const ModIntBase &rhs) & { return *this *= rhs.inv(); }

    friend constexpr ModIntBase operator*(ModIntBase lhs, const ModIntBase &rhs) {
        lhs *= rhs;
        return lhs;
    }
    friend constexpr ModIntBase operator+(ModIntBase lhs, const ModIntBase &rhs) {
        lhs += rhs;
        return lhs;
    }
    friend constexpr ModIntBase operator-(ModIntBase lhs, const ModIntBase &rhs) {
        lhs -= rhs;
        return lhs;
    }
    friend constexpr ModIntBase operator/(ModIntBase lhs, const ModIntBase &rhs) {
        lhs /= rhs;
        return lhs;
    }

    friend constexpr std::istream &operator>>(std::istream &is, ModIntBase &a) {
        i64 i;
        is >> i;
        a = i;
        return is;
    }
    friend constexpr std::ostream &operator<<(std::ostream &os, const ModIntBase &a) {
        return os << a.val();
    }

    friend constexpr bool operator==(const ModIntBase &lhs, const ModIntBase &rhs) {
        return lhs.val() == rhs.val();
    }
    friend constexpr std::strong_ordering operator<=>(const ModIntBase &lhs,
                                                      const ModIntBase &rhs) {
        return lhs.val() <=> rhs.val();
    }

  private:
    U x;
};

template <u32 P> using ModInt = ModIntBase<u32, P>;
template <u64 P> using ModInt64 = ModIntBase<u64, P>;

异或 Trie

struct XorTrie {
    std::vector<std::array<int, 2>> ch;
    int tot = 1;

    XorTrie(int n) : ch(n * 32) {}

    void insert(int x) {
        for (int i = 30, u = 1; i >= 0; i--) {
            int c = (x >> i) & 1;
            if (not ch[u][c]) {
                ch[u][c] = ++tot;
            }
            u = ch[u][c];
        }
    }

    int query(int x) {
        int res = 0;
        for (int i = 30, u = 1; i >= 0; i--) {
            int c = (x >> i) & 1;
            if (ch[u][c ^ 1]) {
                u = ch[u][c ^ 1];
                res |= (1 << i);
            } else {
                u = ch[u][c];
            }
        }
        return res;
    }
};