组合数学
方法一:预处理 + 递推
(C_a^b = C_{a-1}^b + C_{a-1}^{b-1})
时间复杂度:O((n^2))
#include <bits/stdc++.h>
using namespace std;
const int MOD = 1e9 + 7;
const char nl = '
';
const int N = 2000 + 50;
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(){
ios::sync_with_stdio(false);
// cin.tie(nullptr);
init();
int a, b;
while (cin >> a >> b){
cout << c[a][b] << nl;
}
return 0;
}
方法二:预处理 + 快速幂求逆元
(C_a^b = frac{a!}{b!(a-b)!})
时间复杂度:O((nlog{n}))
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MOD = 1e9 + 7;
const char nl = '
';
const int N = 1e5 + 50;
int fact[N], infact[N];
int qmi(int a, int k, int p){
int res = 1;
while (k){
if (k & 1) res = (LL)res * a % p;
a = (LL)a * a % p;
k >>= 1;
}
return res;
}
int main(){
ios::sync_with_stdio(false);
// cin.tie(nullptr);
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 a, b;
while (cin >> a >> b){
cout << (LL)fact[a] * infact[b] % MOD * infact[a - b] % MOD << nl;
}
return 0;
}
方法三:卢卡斯定理
(C_a^b equiv C_{a mod p}^{b mod p} cdot C_{a/p}^{b/p} pmod {p})
时间复杂度:O((plog{p}))
https://www.luogu.com.cn/problem/P3807
题意:求 (C_a^b mod p)
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const char nl = '
';
const int N = 1e5 + 50;
int p;
int qmi(int a, int k){
int res = 1;
while (k){
if (k & 1) res = (LL)res * a % p;
a = (LL)a * a % p;
k >>= 1;
}
return res;
}
int C(int a, int 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(int a, int b){
if (a < p && b < p) return C(a, b);
else return (LL)C(a % p, b % p) * lucas(a / p, b / p) % p;
}
int main(){
ios::sync_with_stdio(false);
// cin.tie(nullptr);
int t;
cin >> t;
while (t--){
int a, b;
cin >> b >> a >> p;
a = a + b;
cout << lucas(a, b) << nl;
}
return 0;
}
方法四:高精度 + 分解质因数
(C_a^b = frac{a!}{b!(a-b)!})
(n!) 里质因子 p 的个数:(lfloor frac{n}{p} floor cdot lfloor frac{n}{p^2} floor cdot ldots cdot lfloor frac{n}{p^k} floor)
#include <bits/stdc++.h>
using namespace std;
const char nl = '
';
const int N = 5000 + 50;
int primes[N], tot;
int sum[N];
bool st[N];
void get_primes(int n){
for (int i = 2; i <= n; ++i){
if (!st[i]) primes[tot++] = i;
for (int j = 0; primes[j] <= n / i; ++j){
st[primes[j] * i] = true;
if (i % primes[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){
vector<int> C;
int t = 0;
for (int i = 0; i < A.size(); ++i){
t += A[i] * b;
C.push_back(t % 10);
t /= 10;
}
while (t){
C.push_back(t % 10);
t /= 10;
}
return C;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
int a, b;
cin >> a >> b;
get_primes(a);
for (int i = 0; i < tot; ++i){
int p = primes[i];
sum[i] = get(a, p) - get(b, p) - get(a - b, p);
}
vector<int> res = {1};
for (int i = 0; i < tot; ++i)
for (int j = 0; j < sum[i]; ++j)
res = mul(res, primes[i]);
for (int i = res.size() - 1; i >= 0; --i) cout << res[i];
cout << nl;
return 0;
}