LeetCode 数学算法技巧全解(含C++实现,持续更新)

在刷 LeetCode 的过程中,我们经常会遇到各种数学题,这类题目往往无法仅靠暴力法取胜,背后的数学原理才是通关关键。本文将详细整理 LeetCode 常见数学技巧,包括理论 + 高质量 C++ 实现。

 1. 乘法逆元(Modular Multiplicative Inverse)

    应用场景

当你需要在模意义下“除以一个数”时,就要用它的 乘法逆元

     通常用于:

  • 组合数取模

  • 模线性方程求解

    C++ 实现(基于费马小定理,模数必须是质数)

int modInverse(int a, int mod) {
    // 快速幂实现 a^(mod-2) % mod
    int res = 1;
    a %= mod;
    int b = mod - 2;
    while (b) {
        if (b & 1) res = 1LL * res * a % mod;
        a = 1LL * a * a % mod;
        b >>= 1;
    }
    return res;
}

 2. 二分快速幂(Binary Exponentiation)

   应用场景

当你需要计算 a^b % mod,但 b 非常大时,用此方法可在 O(log b) 时间内完成。

int fastPow(int a, int b, int mod) {
    int res = 1;
    a %= mod;
    while (b) {
        if (b & 1) res = 1LL * res * a % mod;
        a = 1LL * a * a % mod;
        b >>= 1;
    }
    return res;
}

3. 高斯鞋带定理(Shoelace Formula)

  应用场景

用于计算多边形面积,常见于几何类题目。

double polygonArea(const vector<pair<int, int>>& points) {
    int n = points.size();
    long long area = 0;
    for (int i = 0; i < n; i++) {
        int x1 = points[i].first, y1 = points[i].second;
        int x2 = points[(i + 1) % n].first, y2 = points[(i + 1) % n].second;
        area += 1LL * (x1 * y2 - x2 * y1);
    }
    return abs(area) / 2.0;
}

4. 欧拉函数(Euler's Totient Function)

  应用场景

用于判断一个数 n 小于它且与它互素的整数数量。

int eulerTotient(int n) {
    int res = n;
    for (int i = 2; i * i <= n; i++) {
        if (n % i == 0) {
            while (n % i == 0) n /= i;
            res -= res / i;
        }
    }
    if (n > 1) res -= res / n;
    return res;
}

5. 前缀和(Prefix Sum)

 应用场景

用于快速查询子数组和、区间统计等问题。

vector<int> prefixSum(const vector<int>& nums) {
    int n = nums.size();
    vector<int> prefix(n + 1);
    for (int i = 0; i < n; ++i) {
        prefix[i + 1] = prefix[i] + nums[i];
    }
    return prefix;
}

6. 组合数(Combination)+ 预处理

应用场景

在模意义下计算组合数 C(n,k)C(n, k)C(n,k),多个查询时建议预处理。

const int MOD = 1e9 + 7;
const int MAXN = 1e5 + 10;
int fact[MAXN], invFact[MAXN];

void initComb() {
    fact[0] = invFact[0] = 1;
    for (int i = 1; i < MAXN; ++i) {
        fact[i] = 1LL * fact[i - 1] * i % MOD;
    }
    invFact[MAXN - 1] = modInverse(fact[MAXN - 1], MOD);
    for (int i = MAXN - 2; i >= 1; --i) {
        invFact[i] = 1LL * invFact[i + 1] * (i + 1) % MOD;
    }
}

int comb(int n, int k) {
    if (k < 0 || k > n) return 0;
    return 1LL * fact[n] * invFact[k] % MOD * invFact[n - k] % MOD;
}

7. 同余模定理(Modular Congruence)

 应用场景

用于简化模等式,快速判断两个数模相等。

bool isCongruent(int a, int b, int mod) {
    return (a - b) % mod == 0;
}

8. 约数个数定理(Divisor Count)

 应用场景

用于找出一个数的所有正整数因子的数量。

int countDivisors(int n) {
    int count = 0;
    for (int i = 1; i * i <= n; ++i) {
        if (n % i == 0) {
            count += (i == n / i) ? 1 : 2;
        }
    }
    return count;
}

9. 最大公约数 & 扩展欧几里得(Extended GCD)

 应用场景

解决 ax + by = gcd(a, b) 的整数解,用于求解线性同余方程。

int gcd(int a, int b) {
    return b == 0 ? a : gcd(b, a % b);
}

// 扩展欧几里得
int extendedGCD(int a, int b, int& x, int& y) {
    if (b == 0) {
        x = 1; y = 0;
        return a;
    }
    int d = extendedGCD(b, a % b, y, x);
    y -= a / b * x;
    return d;
}

10. 埃拉托色尼筛法(Sieve of Eratosthenes)

 应用场景

快速生成质数列表,适用于质数计数、质因数分解。

vector<bool> sieve(int n) {
    vector<bool> isPrime(n + 1, true);
    isPrime[0] = isPrime[1] = false;
    for (int i = 2; i * i <= n; ++i) {
        if (isPrime[i]) {
            for (int j = i * i; j <= n; j += i)
                isPrime[j] = false;
        }
    }
    return isPrime;
}

11. 康托展开(Cantor Expansion)

  应用场景

将一个排列映射为整数(排名),常用于状态压缩、唯一标识。

int cantor(const vector<int>& perm) {
    int n = perm.size();
    int res = 0;
    vector<int> used(n, 0);
    vector<int> fact(n, 1);
    for (int i = 1; i < n; ++i) fact[i] = fact[i - 1] * i;

    for (int i = 0; i < n; ++i) {
        int cnt = 0;
        for (int j = 0; j < perm[i]; ++j)
            if (!used[j]) ++cnt;
        res += cnt * fact[n - i - 1];
        used[perm[i]] = 1;
    }
    return res;
}

12. 线性筛法(Linear Sieve)

  应用场景

与埃氏筛不同,线性筛可以在 O(n) 时间内获取质数和最小质因子,适合用于分解质因数等扩展操作。

const int MAXN = 1e6;
int minPrime[MAXN]; // 记录每个数的最小质因数
vector<int> primes;

void linearSieve(int n) {
    for (int i = 2; i <= n; ++i) {
        if (minPrime[i] == 0) {
            minPrime[i] = i;
            primes.push_back(i);
        }
        for (int p : primes) {
            if (p > minPrime[i] || i * p > n) break;
            minPrime[i * p] = p;
        }
    }
}

13. 乘法原理(Multiplication Principle)

  应用场景

用于计数问题,如排列组合构造、密码锁、数字组合问题等。

例如:长度为 n 的数字序列,每位可以从 0~9 中任选,求方案总数。

int countSequences(int n) {
    return pow(10, n); // 10^n 个组合
}

14. 排列组合递推公式

 应用场景

不使用阶乘时快速求组合数(可避免溢出),适合 n 较大时。

int comb(int n, int k) {
    long long res = 1;
    for (int i = 1; i <= k; ++i) {
        res = res * (n - i + 1) / i;
    }
    return (int)res;
}

15. 数位 DP(Digit Dynamic Programming)

  应用场景

用于计数满足特定性质的整数,例如:统计不包含某些数字的数字数量、0-9 出现频率。

int dfs(int pos, int lead, int tight, vector<int>& digits) {
    // 省略具体实现,仅展示用法
    // 递归考虑每一位的取值可能
    return 0;
}
int countValid(int n) {
    vector<int> digits;
    while (n) {
        digits.push_back(n % 10);
        n /= 10;
    }
    reverse(digits.begin(), digits.end());
    return dfs(0, 1, 1, digits);
}

16. 差分数组(Difference Array)

  应用场景

适用于频繁修改某一段区间的值,如:区间 +1 操作、区间覆盖等。

vector<int> getModifiedArray(int length, vector<vector<int>>& updates) {
    vector<int> diff(length + 1, 0);
    for (auto& u : updates) {
        int l = u[0], r = u[1], val = u[2];
        diff[l] += val;
        diff[r + 1] -= val;
    }
    for (int i = 1; i < length; ++i)
        diff[i] += diff[i - 1];
    diff.pop_back(); // 去掉多余项
    return diff;
}

17. 矩阵快速幂(Matrix Fast Power)

  应用场景

常用于求斐波那契数列的第 n 项、线性递推式等问题,时间复杂度 O(logN)。

struct Matrix {
    long long mat[2][2];
    Matrix operator*(const Matrix& other) const {
        Matrix res{};
        for (int i = 0; i < 2; ++i)
            for (int j = 0; j < 2; ++j)
                for (int k = 0; k < 2; ++k)
                    res.mat[i][j] += mat[i][k] * other.mat[k][j];
        return res;
    }
};

Matrix matrixPow(Matrix a, int n) {
    Matrix res = {{{1, 0}, {0, 1}}}; // 单位矩阵
    while (n) {
        if (n & 1) res = res * a;
        a = a * a;
        n >>= 1;
    }
    return res;
}

18. 状态压缩(Bitmask Optimization)

  应用场景

适用于 n 较小(一般 n <= 20)的组合枚举问题,如旅行商问题、集合划分、状态标记

// 统计所有子集的和
int subsetSum(vector<int>& nums) {
    int n = nums.size(), total = 0;
    for (int mask = 0; mask < (1 << n); ++mask) {
        int sum = 0;
        for (int i = 0; i < n; ++i)
            if (mask >> i & 1)
                sum += nums[i];
        total += sum;
    }
    return total;
}

19. 中国剩余定理(Chinese Remainder Theorem, CRT)

  应用场景

求一组同余方程的最小正整数解:

// 求解 x ≡ a1 (mod m1), x ≡ a2 (mod m2)
int CRT(vector<int>& a, vector<int>& m) {
    int n = a.size();
    long long M = 1, res = 0;
    for (int mi : m) M *= mi;
    for (int i = 0; i < n; ++i) {
        long long Mi = M / m[i];
        long long ti = modInverse(Mi % m[i], m[i]);
        res = (res + a[i] * Mi % M * ti % M) % M;
    }
    return (int)(res % M);
}

20. 最小公倍数(LCM)

  应用场景

经常用于模拟轮转、最短时间循环、周期重合类题目。

int lcm(int a, int b) {
    return a / gcd(a, b) * b;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值