• 基础数论--组合数


    C(a,b)表示从a中选b个苹果,0<=b<=a

    n代表询问次数

    根据不同的数据范围有不同的解法

    1、1n10000,1<=b<=a<=2000

      此时n较大,询问次数较多,而a,b较小,可以预处理出所有C(a,b)的值,然后进行查询

      用到了公式C(a,b)=C(a-1,b-1)+C(a-1,b);

      推导:假设总共有a个苹果,从中选b个,则方案数为C(a,b)

         此时我拿出来一个单独考虑,那此时你就有两种方案,而且不会有重合或者漏算,即拿这个苹果或者不拿这个苹果

         那么方案数则为--->拿这个苹果C(a-1,b-1),不拿这个苹果C(a-1,b);

         得证。

     1 #include<iostream>
     2 using namespace std;
     3 const int N=2010,mod=1e9+7;
     4 int C[N][N];
     5 void init(){
     6     for(int i=0;i<N;i++){
     7         for(int j=0;j<=i;j++){
     8             if(j==0) C[i][j]=1;
     9             else C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
    10         }
    11     }
    12 }
    13 int main(void){
    14     init();
    15     int n;
    16     cin>>n;
    17     for(int i=0;i<n;i++){
    18         int a,b;
    19         cin>>a>>b;
    20         cout<<C[a][b]<<endl;
    21     }
    22     return 0;
    23 }

    2、1n10000,1ba1e5

      此时不能预处理出所有C(a,b)了,因为复杂度达到1e10了

      而观察到a,b只有1e5,所以可以预处理出所有的阶乘

      而因为C(a,b)太大,所以需要mod p ,所以可以预处理出阶乘的逆元和阶乘

     1 #include<iostream>
     2 using namespace std;
     3 typedef long long LL;
     4 const int N=1e5+10,mod=1e9+7;
     5 LL fact[N],infact[N];
     6 LL qmi(LL a,LL b,LL mod){
     7     LL res=1;
     8     while(b){
     9         if(b&1){
    10             res=res*a%mod;
    11         }
    12         b>>=1;
    13         a=a*a%mod;
    14     }
    15     return res;
    16 }
    17 void init(){
    18     fact[0]=infact[0]=1;
    19     for(int i=1;i<N;i++){//如果mod不是质数则需要用扩展欧几里得求逆元
    20         fact[i]=fact[i-1]*i%mod;
    21         infact[i]=infact[i-1]*qmi(i,mod-2,mod)%mod;
    22     }
    23 }
    24 int main(void){
    25     init();
    26     int n;
    27     cin>>n;
    28     for(int i=0;i<n;i++){
    29         int a,b;
    30         cin>>a>>b;
    31         cout<<fact[a]*infact[b]%mod*infact[a-b]%mod<<endl;
    32     }
    33     return 0;
    34 }

    3、1n20,1ba1e18,1p1e5

      此时a,b非常大,而观察到p只有1e5,所以可以使用卢卡斯定理求解C(a,b);

      卢卡斯定理:C(a,b)%p=C(a%p,b%p)*C(a/p,b/p);

      可以看出卢卡斯定理转换为程序的话是一个递归的过程

      

     1 #include<iostream>
     2 using namespace std;
     3 typedef long long LL;
     4 LL qmi(LL a,LL b,LL p){
     5     LL res=1;
     6     while(b){
     7         if(b&1){
     8             res=res*a%p;
     9         }
    10         b>>=1;
    11         a=a*a%p;
    12     }
    13     return res;
    14 }
    15 LL C(LL a,LL b,LL p){
    16     LL res=1;
    17     for(int i=a,j=1;i>a-b;i--,j++){
    18         res=res*i%p;
    19         res=res*qmi(j,p-2,p)%p;
    20     }
    21     return res;
    22 }
    23 LL lucas(LL a,LL b,LL p){
    24     if(a<p&&b<p)
    25         return C(a,b,p);
    26     return C(a%p,b%p,p)*lucas(a/p,b/p,p)%p;
    27 }
    28 int main(void){
    29     int n;
    30     cin>>n;
    31     for(int i=0;i<n;i++){
    32         LL a,b,p;
    33         cin>>a>>b>>p;
    34         cout<<lucas(a,b,p)<<endl;
    35     }
    36     return 0;
    37 }

    4、1ba5000

    如果不模某个数,那么这样的组合数是必须写高精度的。

    你可以老老实实写一波高精度乘除

     1 #include<iostream>
     2 #include<vector>
     3 #include<algorithm>
     4 using namespace std;
     5 vector<int> mul(vector<int>& a,int b){
     6     int flag=0;
     7     vector<int> res;
     8     for(int i=0;i<a.size();i++){
     9         flag=flag+a[i]*b;
    10         res.push_back(flag%10);
    11         flag/=10;
    12     }
    13     while(flag){
    14         res.push_back(flag%10);
    15         flag/=10;
    16     }
    17     while(res.size()>0&&res.back()==0){
    18         res.pop_back();
    19     }
    20     return res;
    21 }
    22 
    23 vector<int> div(vector<int>& a,int b){
    24     int r=0;
    25     vector<int> c;
    26     for(int i=a.size()-1;i>=0;i--){
    27         r=r*10+a[i];
    28         c.push_back(r/b);
    29         r=r%b;
    30     }
    31     reverse(c.begin(),c.end());
    32     while(c.size()>1&&c.back()==0)
    33         c.pop_back();
    34     return c;
    35 }
    36 
    37 int main(void){
    38     int a,b;
    39     cin>>a>>b;
    40     vector<int> t;
    41     t.push_back(1);
    42 
    43     for(int i=a;i>a-b;i--){
    44         t=mul(t,i);
    45     }
    46 
    47 
    48 
    49     for(int i=1;i<=b;i++){
    50         t=div(t,i);
    51     }
    52     for(int i=t.size()-1;i>=0;i--){
    53         cout<<t[i];
    54     }
    55     return 0;
    56 }

    或者根据唯一分解定理,可以只考虑最终答案的因子

     1 #include<iostream>
     2 #include<vector>
     3 #include<algorithm>
     4 using namespace std;
     5 const int N=5010;
     6 int primes[N],cnt;
     7 bool st[N];
     8 int sum[N];
     9 void get_primes(int n){
    10     for(int i=2;i<=n;i++){
    11         if(!st[i]){
    12             primes[cnt++]=i;
    13         }
    14         for(int j=0;primes[j]<=n/i;j++){
    15             st[primes[j]*i]=true;
    16             if(i%primes[j]==0) break;
    17         }
    18     }
    19 }
    20 int get(int n,int p){
    21     int res=0;
    22     int t=p;
    23     while(n>=p){
    24         res+=n/p;
    25         p*=t;
    26     }
    27     return res;
    28 }
    29 vector<int> mul(vector<int>& a,int b){
    30     int flag=0;
    31     vector<int> res;
    32     for(int i=0;i<a.size();i++){
    33         flag=flag+a[i]*b;
    34         res.push_back(flag%10);
    35         flag/=10;
    36     }
    37     while(flag){
    38         res.push_back(flag%10);
    39         flag/=10;
    40     }
    41     while(res.size()>0&&res.back()==0){
    42         res.pop_back();
    43     }
    44     return res;
    45 }
    46 
    47 
    48 
    49 int main(void){
    50     int a,b;
    51     cin>>a>>b;
    52     get_primes(max(a,b));
    53     for(int i=0;i<cnt;i++){
    54         sum[i]=get(a,primes[i])-get(b,primes[i])-get(a-b,primes[i]);
    55     }
    56     vector<int> res={1};
    57     for(int i=0;i<cnt;i++){
    58         for(int j=0;j<sum[i];j++){
    59             res=mul(res,primes[i]);
    60         }
    61     }
    62     for(int i=res.size()-1;i>=0;i--) cout<<res[i];
    63     return 0;
    64 }
  • 相关阅读:
    C# 杨辉三角 下
    C# 自动走迷宫 下
    算法练习之1数字填充 下
    动态添加控件并获取其值
    两个ListBox的互动
    VS2005常用快捷键
    GridView内嵌DropDownList操作
    GridView格式化短日期
    获得客户端ID
    Access数据库 Update 语句的怪现象
  • 原文地址:https://www.cnblogs.com/greenofyu/p/14140172.html
Copyright © 2020-2023  润新知