组合数

求解组合数

1.根据二项式定理,求出1~n的所有c(i,k):
c[i][j]的意思为从i中选择j个出来
时间复杂度O(n^2);

memset(C,0,sizeof(C));
for(int i=0;i<=n;i++){
    C[i][0]=1;
    for(int j=1;j<=i;j++) C[i][j]=C[i-1][j-1]+C[i-1][j];
}

2.递推求出c[i]:
根据c(n,k)=(n-k+1)/k+=c(n,k-1)的原理
c[i]的意思是从n中选出i个
时间复杂度为O(n)

C[0]=1;
for(int i=1;i<=n;i++) C[i]=C[i-1]*(n-i+1)/i;

3.预处理求出所有数的阶乘及其逆元,再求出c[i]:
根据c(n,k)=n!/(k!*(n-k)!)
c[i]的意思是从n中选出i个
预处理的时间复杂度为O(n)
求组合数的时间复杂度为O(1);

fac[0]=1;
for(int i=1;i<=maxn;i++) fac[i]=fac[i-1]*i%mod;
inv[maxn]=pow_mod(fac[maxn],mod-2);
for(int i=maxn-1;i>=0;i--) inv[i]=inv[i+1]*(i+1)%mod;

组合数取模——卢卡斯定理

c(n, m)%mod = c(n/mod, m/mod)*c(n%mod, m%mod)%mod

代码

LL Lucas(LL n, LL m, int p){
2         return m ? Lucas(n/mod, m/mod, mod) * comb(n%mod, m%mod, mod) % mod : 1;
3 }

习题 ZOJ - 3624

https://cn.vjudge.net/problem/30525/origin

这道题正向求所有不相交的边不容易,所以我们可以求所有边组合-相交的边组合,得出答案
相交的边的组合数为(A->C)的情况*(B->D)的情况
即不相交的边的组合数=c(n+m,n)*c(m+q-p,q)-c(q+m,q)*c(n+m-p,n)

ac代码

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<algorithm>
#include<iostream>
#include<math.h>
using namespace std;
typedef long long ll;
const ll mod=1e8+7;
const int maxn=2000005;
ll fac[maxn+5];
ll inv[maxn+5];
int n,m,p,q;
ll pow_mod(ll a,ll b){
    if(!b) return 1;
    ll r=pow_mod(a,b/2);
    ll ans=r*r%mod;
    if(b&1) ans=ans*a%mod;
    return ans;
}

ll comb(int a,int b){
    return (fac[a]*inv[b]%mod)*inv[a-b]%mod;
}

int main(){
    fac[0]=1;
    for(int i=1;i<=maxn;i++) fac[i]=fac[i-1]*i%mod;
    inv[maxn]=pow_mod(fac[maxn],mod-2);
    for(int i=maxn-1;i>=0;i--) inv[i]=inv[i+1]*(i+1)%mod;
    while(cin>>m>>n>>p>>q){
        ll ans=(comb(n+m,n)*comb(m-p+q,q)%mod-comb(q+m,q)*comb(n+m-p,n)%mod+mod)%mod;
        cout<<ans<<endl;
    }
}
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页