组合数学专题
公式
C a b = a ! b ! ( a − b ) ! = a × ( a − 1 ) . . . . . . × ( a − b + 1 ) b ! C_{a}^{b} = \frac{a!}{b!(a-b)!}= \frac{a\times (a-1)......\times(a-b+1)}{b!} Cab=b!(a−b)!a!=b!a×(a−1)......×(a−b+1)
1对于数据范围在1000左右
已知公式:
C
a
b
=
C
a
−
1
b
×
C
a
−
1
b
−
1
C_{a}^{b} = C_{a-1}^{b} \times C_{a-1}^{b-1}
Cab=Ca−1b×Ca−1b−1
再利用动态规划的思想将组合数打表。
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std ;
const int N = 2005 ;
const int mod = 1e9 + 7 ;
int c[N][N] ;
int main(){
c[1][0] = 1 ;
c[1][1] = 1 ;
for(int i = 2 ; i <= 2000 ; i ++){
for(int j = 0 ; j <= 2000 ; j ++){
if(!j) {
c[i][j] = c[i-1][j]%mod ;
continue ;
}
c[i][j] = (c[i-1][j]%mod + c[i-1][j-1]%mod)%mod ;
}
}
int n ;
cin >> n ;
while(n --) {
int a ,b ;
cin >> a >> b ;
cout << c[a][b] << endl ;
}
return 0 ;
}
2对于数据范围在 1 0 6 10^{6} 106左右
利用:
C
a
b
=
a
!
b
!
(
a
−
b
)
!
C_{a}^{b} = \frac{a!}{b!(a-b)!}
Cab=b!(a−b)!a!
将每个数阶乘打表出来,并且将每个数阶乘取模的乘法逆元求出来进行打表。
#include <iostream>
#define debug(x) cerr << #x << " : "<< x << endl
using namespace std ;
const int N = 100005 ;
const int mod = 1e9 + 7 ;
typedef long long ll ;
int fact[N] ,incfact[N];
int qmi(int a , int n){
// debug(a) ;
int res = 1 ;
while(n){
if(n&1) res = ((ll)res * a) %mod ;
a = ((ll)a *a)%mod ;
n >>= 1 ;
}
// debug(res) ;
return res ;
}
int main(){
fact[0] = 1 ;
incfact[0] = 1 ;
for(int i = 1 ; i <= 100000 ; i ++){
fact[i] = ((ll)fact[i-1] * i)%mod ;
incfact[i] = ((ll)incfact[i-1] * qmi(i , mod - 2))%mod ;
}
int n ;
cin >> n ;
while(n -- ){
int a ,b ;
cin >> a >> b ;
int up = fact[a] ;
int down = ((ll)incfact[b] * incfact[a-b])%mod ;
int ans = ((ll)up * down)%mod ;
cout << ans << endl ;
}
return 0 ;
}
3对于数据范围在 1 0 18 10^{18} 1018左右
卢卡斯定理:
C
a
b
m
o
d
p
≡
C
a
%
p
b
%
p
×
C
a
/
p
b
/
p
C_{a}^{b}\ mod\ p \equiv C_{a\%p}^{b\%p}\times C_{a/p}^{b/p}
Cab mod p≡Ca%pb%p×Ca/pb/p
#include <iostream>
using namespace std ;
const int N = 100005 ;
typedef long long ll ;
int qmi(int a ,int k ,int p){
int res = 1 ;
while(k){
if(k & 1 ) res = ((ll)res * a) % p ;
k >>= 1 ;
a = ((ll)a * a) % p ;
}
return res ;
}
int C(int a , int b , int p ){
int res = 1 ;
for(int i = 1 ; i <= b ; a -- , i ++){
res = ((ll)res * a) % p ;
res = ((ll)res * qmi(i , p - 2 , p )) % p ;
}
return res ;
}
int lucas(ll a ,ll b ,int p ){
if(a < p && b < p) return C(a , b , p) ;
else return ((ll)C(a%p , b%p , p ) * lucas(a/p , b/p , p)) % p ;
}
int main(){
int n ;
cin >> n ;
while(n -- ){
ll a , b ;
int p ;
cin >> a >> b >> p ;
int ans = lucas(a, b ,p );
cout << ans << endl ;
}
return 0 ;
}