6-组合数学

1027 字
5 分钟
6-组合数学

组合数学#

对于隔板法,要是分隔符之间可以为空,则得把分隔符也视作为元素,比如9人进6个通道,根据顺序和人和通道不同,有A99×C145A^9_9 \times C^5_{14}

组合#

对于数组元素的组合,保证后续的元素不和已经前面组合过的组合即可遍历所有状态空间。从左往右选和不选进行dfs

class Solution {
public:
vector<vector<int>> combine(int n, int k) {
vector<vector<int>> ans;
vector<int> unit;
auto dfs = [&](auto&& dfs, int elem) {
int limit = k - unit.size();
if (limit == 0) {
return;
}
unit.push_back(elem);
if (limit == 1) {
ans.push_back(unit);
}
for (int index = elem + 1; index <= n; ++index) {
dfs(dfs, index);
}
unit.pop_back();
};
for (int i = 1; i <= n; ++i) {
dfs(dfs, i);
}
return ans;
}
};

负数组合#

n + k - 1 可看作虚虚指针的距离

Cnk=(1)kCn+k1kC^k_{-n} = (-1)^k \cdot C^k_{n+k-1}

TODO

Lucas定理#

p为素数,其余为自然数

(mn) mod p=(mpnp)(m mod pn mod p) mod p\begin{pmatrix} m \\ n \end{pmatrix} \space mod\space p = \begin{pmatrix} \lfloor \frac{m}{p} \rfloor \\ \lfloor \frac{n}{p} \rfloor \\ \end{pmatrix} \cdot \begin{pmatrix} m\space mod\space p\\ n\space mod\space p\\ \end{pmatrix} \space mod\space p

实现

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const mod N = 1e5;
const ll mod = 998244353;//其实没什么用,lucas只能用于小质数模数
ll fact[N], ifact[N];
ll qp(ll a, ll b, ll p) {
ll res = 1;
while (b) {
if (b & 1) res = res * a % p;
a = a * a % p;
b >>= 1;
}
return res;
}
void init(int n) {
fact[0] = 1;
for (int i = 1; i <= n; ++i) fact[i] = i * fact[i - 1] % mod;
for (int i = 0; i <= n; ++i) ifact[i] = qp(fact[i], mod - 2, mod);
}
ll c(ll n, ll k) {
if (k > n) return 0;
return fact[n] % mod * ifact[k] % mod * ifact[n - k] % mod;
}
ll lucas(ll n, ll k) {
if (!k) return 1;
return c(n % mod, k % mod) * lucas(n / mod, k / mod) % mod;
}

排列#

st标记环,列举剩下集合非用过的可能的取值进行dfs

TODO

矩阵快速幂#

将快速幂思想迁移到递推式

以斐波那契数列为例

[F[k]F[k1]]=[1110]k1[10]=Ak1[10],k=1n\begin{bmatrix} F[k] \\ F[k-1] \end{bmatrix} = \begin{bmatrix} 1 & 1 \\ 1 & 0 \end{bmatrix} ^{k-1} \begin{bmatrix} 1 \\ 0 \end{bmatrix} =A^{k-1} \begin{bmatrix} 1 \\ 0 \end{bmatrix} ,\quad k=1 \ldots n

使用矩阵快速幂的前提是所求值可化为上一列各元素的线性组合,需要是常数,以F[i]=(ai+b)×qiF[i]=(ai+b)\times q^i为例有

[F[k]ak]=[qq0q]k1[ba],k=1n\begin{bmatrix} F[k] \\ a^k \end{bmatrix} = \begin{bmatrix} q & q \\ 0 & q \end{bmatrix} ^{k-1} \begin{bmatrix} b\\ a \end{bmatrix} ,\quad k = 1\ldots n
using ll = long long;
vector<vector<ll>> mul(const vector<vector<ll>>& a, const vector<vector<ll>> b, ll mod) {
int n = a.size(), mid = a[0].size(), m = b[0].size();
vector<vector<ll>> res(n, vector<ll>(m, 0));
for (int i = 0; i < n; ++i) {
for (int j = 0; j < m; ++j) {
for (int k = 0; k < mid; ++k) {
res[i][j] = (res[i][j] + a[i][k] * b[k][j]) % mod;
}
}
}
return res;
}
vector<vector<ll>> mmi(vector<vector<ll>> A, ll b, ll mod) {
vector<vector<ll>> res = {{1 % mod, 0}, {0, 1 % mod}};
while (b) {
if (b & 1) res = mul(res, A, mod);
A = mul(A, A, mod);
b >>= 1;
}
return res;
}

对于特殊的前缀和,也可以用矩阵快速幂进行计算,以

F[n]=i=1n(ai+b)×qiF[n] = \sum_{i = 1}^{n}{(ai+b) \times q^i}

为例,相对于F[i]=(ai+b)×qiF[i]=(ai+b)\times q^i需要多留一行给求和积累效果

[F[k1]f[k]ak]=[1100qq00q]k1[0ba],k=1n\begin{bmatrix} F[k - 1] \\ f[k] \\ a^k \end{bmatrix} = \begin{bmatrix} 1 & 1 & 0 \\ 0 & q & q \\ 0 & 0 & q \end{bmatrix} ^{k-1} \begin{bmatrix} 0\\ b \\ a \end{bmatrix} ,\quad k = 1\ldots n

总结来说,矩阵快速幂有以下几个值得注意的点

  1. 前缀和只有其本身和组成单位有关,权为1
  2. 等比数列只和其所在列有关,权为q
  3. iqiiq^i和其所在列和对应和等比数列有关,权都为q
  4. 等差数列可看作常数C的前缀和

可见,矩阵快速幂对若干个等差数列和等比数列的线性组合和前缀和都适用,其他并不适用

矩阵快速幂如果用二维vector或者struct自行实现会被卡常,需要转化为维护变换的几个值和ans的几个值,

这里上三角矩阵的幂仍然是上三角矩阵,故变换可等价为右上角的6个值的递归,由于对角线独立幂化,故最左上角的1为定值1

下三角矩阵和上三角矩阵性质相同,而反上下三角矩阵性质则大有不同

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

6-组合数学
https://skaco2.com/posts/01-algorithm/6-组合数学/
作者
SKACO2
发布于
2026-04-03
许可协议
CC BY-NC-SA 4.0

评论区

Profile Image of the Author
SKACO2
Hello……
公告
欢迎来到我的博客!
音乐
封面

音乐

暂未播放

0:00 0:00
暂无歌词
分类
标签
站点统计
文章
53
分类
8
标签
54
总字数
58,255
运行时长
0
最后活动
0 天前

目录