求组合数有四种方法
从 n 个不同元素中取出 m 个元素的所有组合的数目
公式
性质
(1) C ( n , m ) = C ( n , n - m ) = C ( n - 1 , m - 1 ) + C ( n - 1 , m ) (2)
实现方法1(杨辉三角)
杨辉三角第 i 行第 j 列对应 C ( i ,j ) 的结果 用动态规划求从 n 个元素中取出 m 个元素的所有组合个数 c [ i ] [ j ] :在 i 个元素中选 j 个 对于当前状态 i j ,可以由两种情况转移而来: 1、前 i - 1 个元素选了 j 个 1、前 i - 1 个元素选了 j - 1 个 即 c [ i ] [ j ] = c [ i - 1 ] [ j ] + c [ i - 1 ] [ j - 1 ] 预处理 c [ i ] [ 0 ] = c [ i ] [ i ] = 1 ( 左右边界 1 )
//求组合数一(递推)
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 2010;
const int mod = 1e9 + 7;
int c[N][N];
void init() {
for (int i = 0; i < N; i++) {
for (int j = 0; j <= i; j++) {
if (!j) {
c[i][j] = 1;
}
else c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod;
}
}
}
int main() {
init();
int n;
cin >> n;
while ( n -- ) {
int a, b;
cin >> a >> b;
printf("%d\n", c[a][b]);
}
return 0;
}
实现方法2(逆元)
乘法逆元 :( a / b ) % mod = a * ( b ^ ( mod - 2 ) ) % mod, mod为质数 b 的逆元就是 b ^ ( mod - 2 )
//求组合数二(预处理阶乘||逆元)
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N = 100010, mod = 1e9 + 7;
int fact[N], infact[N];
int qmi(int a, int b, int k) {
int res = 1;
while (b) {
if (b & 1) res = (ll)res * a % k;
a =(ll) a * a % k;
b >>= 1;
}
return res;
}
int main() {
fact[0] = infact[0] = 1;
for (int i = 1; i < N; i++) {
fact[i] = (ll)fact[i - 1] * i % mod;
infact[i] = (ll)infact[i - 1] * qmi(i, mod - 2, mod) % mod;
}
int n;
scanf("%d", &n);
while (n--) {
int a, b;
scanf("%d%d", &a, &b);
printf("%d\n",(ll) fact[a] * infact[b] % mod * infact[a - b] % mod );
}
return 0;
}
实现方法3(卢卡斯定理)
卢卡斯定理:Cab≡Ca mod pb mod pCa/pb/p(mod p)
//求组合数三(卢卡斯定理)
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
int p;
int qmi(int a, int b) {
int res = 1;
while (b) {
if (b & 1)res = (ll)res * a % p;
a = (ll)a * a % p;
b >>= 1;
}
return res;
}
int c(ll a, ll b) {
int res = 1;
for (int i = 1, j = a; i <= b; i++, j--) {
res = (ll)res * j % p;
res = (ll)res * qmi(i, p - 2) % p;
}
return res;
}
int Lucas(ll a, ll b) {
if (a < p && b < p)return c(a, b);
return (ll)c(a % p, b % p) * Lucas(a / p, b / p) % p;
}
int main() {
int n;
cin >> n;
while (n--) {
ll a, b;
cin >> a >> b >> p;
cout << Lucas(a, b) << endl;
}
return 0;
}
4.分解质因数法
当我们需要求出组合数的真实值,而非对某个数的余数时,分解质因数的方式比较好用:
1.筛法求出范围内的所有质数
2.通过 C(a, b) = a! / b! / (a - b)! 这个公式求出每个质因子的次数。 n! 中p的次数是 n / p + n / p^2 + n / p^3 + …
3.用高精度乘法将所有质因子相乘
//求组合数四(分解质因数)
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long ll;
const int N = 5010;
int prime[N], cnt;
int sum[N];
bool st[N];
void get_prime(int n) {
for (int i = 2; i <= n; i++) {
if (!st[i])prime[cnt++] = i;
for (int j = 0; prime[j] <= n / i; j++) {
st[prime[j] * i] = true;
if (i % prime[j] == 0)break;
}
}
}
int get(int n, int p) {
int res = 0;
while (n) {
res += n / p;
n /= p;
}
return res;
}
vector<int> mul(vector<int> a, int b) {
}
int main() {
int a, b;
cin >> a>> b;
get_prime(a);
for (int i = 0; i < cnt; i++) {
int p = prime[i];
sum[i] = get(a, p) - get(b, p) - get(a - b, p);
}
vector<int>res;
res.push_back(1);
for (int i = 0; i < cnt; i++) {
}
}