組合數學——組合數問題(四類匯總)


組合數問題一:

給定n組詢問,每組詢問給定兩個整數ab,請你輸出的值。

1n10000,
1ba2000

  模板題鏈接:組合數一

問題特點:數據組數較多,a,b的范圍較小,且要求對一個定值取模。

解決方法:楊輝三角

  因為a,b范圍非常小,直接利用楊輝三角打表即可。

代碼實現:

#include <cstdio>
#include <algorithm>
#include <iostream>
using namespace std;
const int N = 2010, mod = 1e9+7;
int s[N][N];

void start()
{
    s[0][0]=1;
    for(int i=1;i<=N-5;i++)
        for(int j=1;j<=i;j++)
            s[i][j]=(s[i-1][j]+s[i-1][j-1])%mod;
}

int main()
{
    start();
    int n;scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        printf("%d\n",s[a+1][b+1]);
    }
    return 0;
}

 


 組合數問題二

給定n組詢問,每組詢問給定兩個整數ab,請你輸出的值。

1≤n≤10000,
1ba105

  模板題鏈接:組合數二

問題特點:數據組數較多,a,b范圍較大,且要求對一個定值取模。

解決方法:乘法逆元

  利用乘法逆元將a/b mod p轉化成a*b-1 mod p。

  然后將階乘與階乘的逆元分別打表即可。

  遞推式:

① n! = (n-1)! * n

② n!-1 = (n-1)!-1 * np-2

②的證明:

  (n-1)!-1 = (n-1)!p-2

  (n-1)!p-2 * np-2 = n!p-2

代碼實現:

#include <iostream>
#include <cstdio>

using namespace std;
typedef long long ll;
const int N = 100000+10, mod = 1e9 + 7;

int jc[N],ny[N];

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

}

void start()
{
    jc[0]=1;ny[0]=1;
    for(int i=1;i<=N-5;i++)
    {
        jc[i]=(ll)jc[i-1]*i%mod;
        ny[i]=(ll)ny[i-1]*qmi(i,mod-2,mod)%mod;
    }
}
int main()
{
    start();
    int n;scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        int a,b;scanf("%d%d",&a,&b);
        int ans=(ll)jc[a]*ny[b]%mod*ny[a-b]%mod;
        printf("%d\n",ans%mod);
    }
    return 0;
}

 


 組合數問題三

給定n組詢問,每組詢問給定三個整數a,b,p,其中p是質數,請你輸出的值

1n20,
1ba1018,
1p105

  模板題鏈接:組合數三

問題特點:數據組數較少,a,b范圍很大,p的值非定值。

解決方法:Lucas定理

  若p是質數,則對於任意整數1≤m≤n,有:

  

  相當於是把n和m表示成p進制數,對p進制下的每一位分別計算組合數,最后再乘起來。

  利用Lucas定理將目標組合數遞歸分解,直到m和n小於p為止。

  然后對於每個分解出來的m和n的范圍均在p以內的組合數,就可以放心地根據組合數的定義直接求解。

  當然,對於每一步計算過程,都需要對p取模,遇到除法的利用快速冪轉化成乘法逆元即可。

代碼實現:

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;

int p; //用的次數比較多的變量,一般設置的變量名比較短,且一般設成全局變量

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

int lucas(ll a,ll b)
{
    if(a<p&&b<p)return C(a,b);
    {
        return (ll)C(a%p,b%p)*lucas(a/p,b/p)%p;
    }
}

int main()
{
    int n;scanf("%d",&n);
    while(n--)
    {
        ll a,b;
        scanf("%lld%lld%d",&a,&b,&p);  //scanf是c的語法,cin是c++的語法,函數內部自帶&
        printf("%d\n",lucas(a,b));
    }
    return 0;
}

 


組合數問題四

輸入a,b,求的值。

注意結果可能很大,需要使用高精度計算。

1ba5000

  模板題鏈接:組合數四

問題特點:只有一組數據,a,b取值不大,但沒有要求取模。

解決方法:高精度

  題目沒有要求取模,就沒辦法在過程中保證數據的大小,只能暴力高精度。

  根據組合數的定義,可得:

  

  在循環過程中一邊乘一邊除即可,這樣便只需要寫兩個函數:高精度乘低精度,高精度除以低精度。

代碼實現:

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
struct largeint{
    int len;
    int num[10010];
};
largeint lar_mult(largeint a,int b)
{
    for(int i=1;i<=a.len;i++)a.num[i]*=b;
    for(int i=1;i<=a.len;i++)
    {
        a.num[i+1]+=a.num[i]/10;
        a.num[i]%=10;
    }
    if(a.num[a.len+1]>0)a.len++;
    while(a.num[a.len]>9)
    {
        a.len++;
        a.num[a.len]+=a.num[a.len-1]/10;
        a.num[a.len-1]%=10;
    }
    while(a.num[a.len]==0&&a.len>1)a.len--;
    return a;
}

largeint lar_div(largeint a,int b)
{
    for(int i=a.len;i>=1;i--)
    {
        if(i>1)a.num[i-1]+=(a.num[i]%b)*10;
        a.num[i]/=b;
    }
    while(a.num[a.len]==0&&a.len>1)a.len--;
    return a;
}

void print(largeint n)
{
    for(int i=n.len;i>=1;i--)
        printf("%d",n.num[i]);
    printf("\n");
    return;
}

int main()
{
    int a,b; scanf("%d%d",&a,&b);
    largeint res;
    res.num[1]=1;res.len=1;
    for(int i=a,j=1;j<=b;i--,j++)
    {
        res=lar_mult(res,i);
        res=lar_div(res,j);
    }
    print(res);
    return 0;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM