Walk--求组合数

Walk

问题:求组合数C(n, m)(n在下,m再上)
先了解一下两个小知识

费马小引理

在这里插入图片描述
这个可以用来求特殊情况下的逆元。


逆元

定义:如果 a * x = 1(mod p),且gcd(a, p) = 1(等价于a,p互质),那 x 就为模 p 意义下 a 的乘法逆元。

根据上面的费马小引理,我们可以的得知假如 p 是素数,那a^(p-1) = 1(mod p).成立
那a^(p-2) 就是模 p 意义下 a 的逆元(只适用于 p 是素数,下次补篇求逆元的博客)


用逆元求 除法取模

除法不能直接取模
小定理:a/b % p = a*x % mod p= 1 % p;(x为模p意义下a的逆元)
证明:a/b * b * x = a * x (mod p) ( b * x = 1(mod p) );
这样就解决了求组合数之间的除法了。


分段讲代码:
快速幂模板

ll Power(ll x, ll y) {
    
    
    ll n = y, ans = 1, res = x;
    while(n){
    
    
        if(n & 1) ans = ans * res % mod;
        n >>= 1;
        res = res * res % mod;
    }
    return ans%mod;
}

初始化:求各个阶乘(mod p),和各个阶乘在模p意义下的逆元。

for(int i=1; i<=N; i++) C[i] = (C[i-1]*i) % mod;
inv[N-1] = Power(C[N-1], mod-2); //费马小引理求逆元。
for(int i=N-2; i>=0; i--) {
    
    
	inv[i] = inv[i+1]*(i+1) % mod; //阶乘逆元递推关系。
}

解释一下阶乘的递推公式 ( k ! ) − 1 (k!)^{-1} (k!)1表示 k k k的阶乘的逆
( k ! ) − 1 = ( ( k − 1 ) ! ∗ k ) − 1 = ( k − 1 ) − 1 ∗ k − 1 (k!)^{-1}=((k-1)!*k)^{-1}=(k-1)^{-1}*k^{-1} (k!)1=((k1)!k)1=(k1)1k1
所以: ( k ! ) − 1 ∗ k = ( ( k − 1 ) ! ) − 1 (k!)^{-1}*k = ((k-1)!)^{-1} (k!)1k=((k1)!)1


最终求解组合数

ll U(ll x, ll y) {
    
    
	if(y > x || x < 0 || y < 0) return 0;
	if(y == 0 || x == y) return 1;
	//A(x,x) / (A(y,y) * A(x-y,x-y),转化成逆元相乘
	return (C[x] * inv[y]%mod * inv[x-y]) % mod;
}

总代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1e9+7;
const ll N = 2e6+5;
ll C[N] = {
    
    1};
ll inv[N];
ll Power(ll x, ll y) {
    
    
    ll n = y, ans = 1, res = x;
    while(n){
    
    
        if(n & 1) ans = ans * res % mod;
        n >>= 1;
        res = res * res % mod;
    }
    return ans%mod;
}

ll U(ll x, ll y) {
    
    
	if(y > x || x < 0 || y < 0) return 0;
	if(y == 0 || x == y) return 1;
	return (C[x] * inv[y]%mod * inv[x-y]) % mod;
}
int main() {
    
    
    for(int i=1; i<=N; i++) C[i] = (C[i-1]*i) % mod;
    inv[N-1] = Power(C[N-1], mod-2);
    for(int i=N-2; i>=0; i--) {
    
    
    	inv[i] = inv[i+1]*(i+1) % mod; 
	}
    ll t, a, b, temp;
    scanf("%lld", &t);
    while(t--) {
    
    
        scanf("%lld%lld", &a, &b);
        a--, b--;
        a += b;
        printf("%lld\n", U(a, b));
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_45363113/article/details/106754025
今日推荐