C[a][b]:从a里面选b个的方案
递推:C[a][b] = C[a-1][b]+C[a-1][b-1] 可以处理到a,b属于[1, 1e4]
公式:C[a][b] = fact[a] * infact[b] * infact[a - b] (fact[a]表示a的阶乘,infact[a]表示a!的逆元)可以处理到a,b属于[1, 1e8]
lucas定理:C[a][b]=C[a%p][b%p] * C[a/p][b/p] 适用于a和b很大,但是p比较小的情景
组合恒等式:
C[n][0]+C[n][1]+…+C[n][n] = 2 ^ n
排列数
A[a][b] = fact[a!] / fact[(a-b)!]
定义: 假设有n个元素,n个位置,每个元素都有自己唯一的正确位置,问,所有元素都处在错误位置有多少可能
公式
设f(n) 表示n个元素的错排种数,则f(n)=(n-1)∗(f(n - 1)+f(n − 2))
f(0) = 1, f(1) = 0, f(2) = 1
卡特兰数序列
H0 | H1 | H2 | H3 | H4 | H5 | H6 |
---|---|---|---|---|---|---|
1 | 1 | 2 | 5 | 14 | 42 | 132 |
公式
$ H(n) = \frac{C[2n][n]}{n+1} (n>=2, n∈N+)$
H(n)=\begin{cases}
\sum_{i=1}^n H(i-1)H(n-i), n>=2, n∈N+\\
1, n = 0, 1
\end{cases}
$ H(n) = \frac{H(n-1) * (4n - 2)}{n+1} (n>=2, n∈N+)$
$ H(n) = C[2n][n] - C[2n][n-1]$
处理的问题
/*
本题的数据量比较小,a和b均为[1, 2000],询问1e4次
可以先把c[a][b]预处理出来,计算4e6次,然后每次O(1)得到答案
*/
#include
using namespace std;
int const N = 2e3 + 10, mod = 1e9 + 7;
int c[N][N]; // 记录答案
int n;
// 预处理
void init() {
for (int i = 0; i < N; ++i)
for (int j = 0; j <= i; ++j)
if (j == 0) c[i][j] = 1;
else c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod;
}
int main() {
init(); // 预处理
cin >> n;
for (int i = 0; i < n; ++i) {
int a, b;
scanf("%d %d", &a, &b);
printf("%d\n", c[a][b]);
}
return 0;
}
/*
本题的a,b数据量为[1, 1e5],可以预处理出阶乘,fact[a]表示a的阶乘,infact[a]表示a!的逆元
那么 fact[i] = (LL)fact[i - 1] * i % mod;
infact[i] = (LL)infact[i - 1] * qmi(i, mod - 2, mod) % mod;
每次询问是可以按照公式fact[a] * infact[b] * infact[a - b]来O(1)计算出C[a][b]
*/
#include
using namespace std;
typedef long long LL;
int const N = 1e5 + 10, mod = 1e9 + 7;
int fact[N], infact[N];
int n;
LL qmi(LL a, LL k, LL p) {
LL res = 1;
while (k) {
if (k & 1) res = res * a % p;
k >>= 1;
a = a * a % p;
}
return res;
}
// 预处理
void init() {
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 main() {
init(); // 预处理
// 计算
cin >> n;
for (int i = 0; i < n; ++i) {
int a, b;
scanf("%d %d", &a, &b);
printf("%d\n", (LL)fact[a] * infact[b] % mod * infact[a - b] % mod);
}
return 0;
}
/*
本题的a,b数据量为[1, 1e18],但是p的数据量为[1, 1e5]
那么可以使用lucas来处理,C[a][b] = C[a % p][b % p] * C[a / p][b / p]
每次询问的时候可以先预处理出fact[a],infact[a]等参数,然后用lucas公式来处理,
这样时间复杂度为20*1e5*60
*/
#include
using namespace std;
typedef long long LL;
int const N = 1e5 + 10;
LL fact[N], infact[N];
int n;
LL qmi(LL a, LL k, LL p) {
LL res = 1;
while (k) {
if (k & 1) res = res * a % p;
k >>= 1;
a = a * a % p;
}
return res;
}
// 预处理
void init(LL p) {
fact[0] = infact[0] = 1;
for (int i = 1; i < N; ++i) {
fact[i] = fact[i - 1] * i % p;
infact[i] = infact[i - 1] * qmi(i, p - 2, p) % p;
}
}
// 计算C[a][b]
LL C(LL a, LL b, LL p) {
return fact[a] * infact[b] % p * infact[a - b] % p;
}
// lucas来处理,适用于p比较小的情况
LL lucas(LL a, LL b, LL p) {
if (a < p && b < p) return C(a, b, p);
else return C(a % p, b % p, p) * lucas(a / p, b / p, p) % p;
}
int main() {
// 计算
cin >> n;
for (int i = 0; i < n; ++i) {
memset(fact, 0, sizeof fact);
memset(infact, 0, sizeof infact);
LL a, b, p;
scanf("%lld %lld %lld", &a, &b, &p);
init(p); // 预处理出fact[a], infact[a]等数据
printf("%lld\n", lucas(a, b, p));
}
return 0;
}
/*
本题没有取模,那么C[a][b]按照公式算出来会很大,因此需要高精度
然后要是按照公式C[a][b]=(a!) / (b!(a - b)!)这样子算法会很慢,所以考虑把阶乘质因数分解,然后对于
每个质因子的乘积用高精度去运算,这样的时间复杂度为O(n)
*/
#include
using namespace std;
typedef long long LL;
int const N = 5e3 + 10;
int prime[N], cnt, st[N];
// 筛素数
void init(int n) {
for (int i = 2; i <= n; ++i) {
if (!st[i]) prime[cnt++] = i;
for (