C(a,b)表示从a中选b个苹果,0<=b<=a
n代表询问次数
根据不同的数据范围有不同的解法
1、1≤n≤10000,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、1≤n≤10000,1≤b≤a≤1e5
此时不能预处理出所有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、1≤n≤20,1≤b≤a≤1e18,1≤p≤1e5
此时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、1≤b≤a≤5000
如果不模某个数,那么这样的组合数是必须写高精度的。
你可以老老实实写一波高精度乘除
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 }