保研机试训练Day-21

组合数

Posted by Jeffzzc on April 13, 2025

数论

组合数1(递推法)

给定 n 组询问,每组询问给定两个整数 ab请你输出 C(a,b) mod(10^9+7) 的值。

#include<iostream>
#include<algorithm>
using namespace std;

const int N = 2010, mod = 1e9 + 7;
int c[N][N];
int n;

void init(){
    for(int i=0;i<=2000;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();
    scanf("%d",&n);
    while(n--){
        int a,b;
        scanf("%d%d",&a,&b);
        printf("%d\n",c[a][b]);
    }
    return 0;
}

组合数2(预处理)

给定 n 组询问,每组询问给定两个整数 ab请你输出 C(a,b) mod(10^9+7) 的值。

#include<iostream>
#include<algorithm>
using namespace std;

typedef long long ll;
const int N = 100010, mod = 1e9 + 7;
int fact[N],infact[N];

int qmi(int a,int k,int p){
    int res = 1;
    while(k){
        if(k & 1) res = (ll)res * a % p;
        a = (ll)a * a % p;
        k >>= 1;
    }
    return res;
}

int main(){
    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 n;
    scanf("%d",&n);
    while(n--){
        int a,b;
        scanf("%d%d",&a,&b);
        printf("%d\n",(ll)fact[a] * infact[a-b] % mod * infact[b] % mod);
    }
    return 0;
}

组合数3(少组数数据量大) Lucas定理

给定 n 组询问,每组询问给定三个整数 a,b,p,其中 p 是质数,请你输出 C(a,b) mod p 的值。

#include<iostream>
#include<algorithm>
using namespace std;

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;
        a = (ll)a * a % p;
        k >>= 1;
    }
    return res;
}

int C(int a,int b,int p){
    if(b > a) return 0;
    int res = 1;
    for(int i=1,j=a;i<=b;i++,j--){
        res = (ll)res * j % 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;
        cout << lucas(a,b,p) << endl;   
    }
    return 0;
}

组合数4(高精度)

请你输出 C(a,b) 的值。

#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
const int N = 5010;
int primes[N],cnt,sum[N];
bool st[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;
    cin >> a >> b;
    get_primes(a);
    for(int i=0;i<cnt;i++){
        int p = primes[i];
        sum[i] = get(a, p) - get(b, p) - get(a-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;
}