组合数 Ⅰ
递推式:
C a b = C a − 1 b + C a − 1 b − 1 C_a ^b = C_{a-1} ^ b + C_{a-1} ^ {b-1} Cab=Ca−1b+Ca−1b−1
C a 0 = 1 C_a ^ 0 = 1 Ca0=1
#include <iostream>
#include <cstring>
#include <algorithm>
//像极了dp。。
using namespace std;
const int N = 2100, 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;
scanf("%d",&n);
while(n--){
int a,b;
scanf("%d%d",&a,&b);
printf("%d\n",c[a][b]);
}
return 0;
}
组合数Ⅱ
C a b = a ! b ! ∗ ( a − b ) ! C_a ^ b = \frac{a!}{b! * (a-b)!} Cab=b!∗(a−b)!a!
除法需要通过求逆元的方式,即 f a c t ( a ) ∗ i n f a c t ( b ) ∗ i n f a c t ( ( a − b ) ) fact(a) * infact(b) * infact((a-b)) fact(a)∗infact(b)∗infact((a−b))
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10, mod = 1e9 + 7;
int fact[N];//阶乘
int infact[N];//逆元
int qmi(int a, int k, int p){
ll res = 1 % p;
while(k){
if(k&1) res = (1ll)* res * a % p;
a = (1ll) * a * a % p;
k >>= 1;
}
return res;
}
int main(){
//预处理
fact[0] = infact[0] = 1;
for(int i = 1; i < N; i++){
fact[i] = (1ll) * fact[i-1]*i%mod;
infact[i] = (1ll)*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",(1ll) * fact[a] * infact[b] % mod * infact[a- b]%mod);
}
}
组合数 Ⅲ
卢卡斯定理
C a b = C a m o d p b m o d p ∗ C a / p b / p m o d p C_a^b = C_{a \ mod \ p} ^ {b \ mod \ p} * C_{a/p} ^ {b/p} \ mod \ p Cab=Ca mod pb mod p∗Ca/pb/p mod p
除法部分,通过快速幂求逆元 来解决
时间复杂度
T ∗ p ∗ l o g p N ∗ l o g 2 p T*p*log_p N * log_2 p T∗p∗logpN∗log2p,T等于多少组(20),N为 1 < = b < = a < = 1 e 18 1<=b <=a <=1e^{18} 1<=b<=a<=1e18 ,p最大 1 e 5 1e^5 1e5
因当p等于 1 e 5 1e^5 1e5时, l o g p N log_p N logpN 等于3~4
20 ∗ 1 e 5 ∗ 20 约 等 于 4 e 7 20 * 1e^5 * 20 约等于 4e^7 20∗1e5∗20约等于4e7
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
int n;
int p;
int qmi(int a, int k){
ll res = 1 % p;
while(k){
if(k&1) res = (1ll) * res * a % p;
a = (1ll) * a * a % p;
k >>= 1;
}
return res;
}
int C(int a, int b){
if(b > a) return 0;
int res = 1;
for(int i = 1, j = a; i <= b; i++,j--){
res = (1ll)*res * j % p;
res = (1ll) * res * qmi(i,p-2) % p;
}
return res;
}
int lucas(ll a, ll b){
if(a < p && b < p) return C(a,b);
return (1ll)*C(a%p,b%p) * lucas(a/p,b/p)%p;
}
int main(){
scanf("%d",&n);
while(n--){
ll a,b;
scanf("%lld%lld%d",&a,&b,&p);
cout << lucas(a,b) << endl;
}
return 0;
}
组合数 Ⅳ
因限制了64M的内存,所以开C[5010][5010]的数组会爆内存
正确做法
:
1.筛素数(1~5000)
2.求每个质数的次数
3.用高精度乘法吧所有质因子乘起来
C a b = a ∗ ( a − 1 ) ∗ . . . ∗ ( a − b + 1 ) b ∗ ( b − 1 ) ∗ . . . ∗ 1 C_a^b = \frac{a*(a-1)*...*(a-b+1)}{b*(b-1)*...*1} Cab=b∗(b−1)∗...∗1a∗(a−1)∗...∗(a−b+1)
= a ∗ ( a − 1 ) ∗ . . . ∗ ( a − b + 1 ) ∗ ( a − b ) ! b ∗ ( b − 1 ) ∗ . . . ∗ 1 ∗ ( a − b ) ! =\frac{a*(a-1)*...*(a-b+1) * (a - b)!}{b*(b-1)*...*1 * (a-b)!} =b∗(b−1)∗...∗1∗(a−b)!a∗(a−1)∗...∗(a−b+1)∗(a−b)!
= a ! b ! ∗ ( a − b ) ! = \frac{a!}{b!*(a-b)!} =b!∗(a−b)!a!
分解质因数后:
= p 1 a 1 ∗ p 2 a 2 ∗ . . . ∗ p k a k =p_1^{a_1}*p_2^{a_2}*...*p_k^{a_k} =p1a1∗p2a2∗...∗pkak
求分子分解 每个p的次数具体见大佬博客,%%%大佬
答案为: ∏ i P i 分 子 次 数 和 − 分 母 次 数 和 \prod_i P_i^{分子次数和-分母次数和} ∏iPi分子次数和−分母次数和
不是很理解,粗糙的走了个过场
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
const int N = 6e3 + 10;
int primes[N],cnt;
bool st[N];
int sum[N];
void get_primes(int n){
for(int i = 2; i <= n; i++){
if(!st[i]) primes[cnt++] = 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(){
int a,b;
scanf("%d%d",&a,&b);
get_primes(a);
for(int i = 0 ; i < cnt; i++){
int p = primes[i];
sum[i] = get(a,p) - get(a-b,p) - get(b,p);
}
vector<int> res;
res.push_back(1);
for(int i = 0; i < cnt; i++){
for(int j = 0; j < sum[i]; j++){
res = mul(res,primes[i]);
}
}
for(int i = res.size() - 1; i >= 0; i--) printf("%d",res[i]);
puts("");
return 0;
}