基础数论--组合数

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 }

 

posted on 2020-12-15 18:04  greenofyu  阅读(189)  评论(0编辑  收藏  举报