组合数专题

4 篇文章 0 订阅
2 篇文章 0 订阅

 

shuoj1937-组合数level 0

 

Description
求C(n,m)对1000000007取模后的值,已知公式如下

 

Input
第一行有一个整数T,表示有T组数据(T≤1000)

接下来有T行,每行有两个整数n,m,用空格隔开。

0≤n≤20,0≤m≤n

Output
对于每组数据,输出答案并换行


Sample Input
2
4 2
5 2

Sample Output
6
10

题解::通过看题本题的n,m<=20,可以应用组合数的知识直接求解。但有点要注意mod不满足除法,不能在求阶乘时mod。

代码::

#include <iostream>
#include <sstream>
#include <ios>
#include <iomanip>
#include <functional>
#include <algorithm>
#include <vector>
#include <string>
#include <list>
#include <queue>
#include <deque>
#include <stack>
#include <set>
#include <map>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <climits>
#include <cctype>
using namespace std;
#define XINF INT_MAX
#define INF 0x3FFFFFFF
#define MP(X,Y) make_pair(X,Y)
#define PB(X) push_back(X)
#define REP(X,N) for(int X=0;X<N;X++)
#define REP2(X,L,R) for(int X=L;X<=R;X++)
#define DEP(X,R,L) for(int X=R;X>=L;X--)
#define CLR(A,X) memset(A,X,sizeof(A))
#define IT iterator
typedef long long ll;
typedef pair<int,int> PII;
typedef vector<PII> VII;
typedef vector<int> VI;
const int MOD = 1e9+7;
ll factorial(ll a)
{
	ll fac = 1,cnt = a;
	for(int i = 1;i<cnt;i++){
		fac = fac*a;
		a -=1;
	}
	return fac;	
}
int main(){
	int T;
	cin>>T;
	while(T--)
	{
		ll a,b;
		cin>>a>>b;
		ll fa,fb,fc;
		fa = factorial(a);
		fb = factorial(b);
		fc = factorial(a - b);
		cout<<(fa/fb/fc)%MOD<<endl;
	}
	return 0;
}

shuoj1938-组合数level 1

Description
求C(n,m)对1000000007取模后的值,已知公式如下
Input
第一行有一个整数T,表示有T组数据(T≤10^5)

接下来有T行,每行有两个整数n,m,用空格隔开。0≤n≤1000,0≤m≤n

Output
对于每组数据,输出答案并换行
Sample Input
2
4 2
5 2
Sample Output
6
10
HINT
输入和输出的数据规模较大,请使用scanf和printf
Source
xyiyy
题解:: 这道题很明显的不同之处(1)数据量变大了(2)n,m的值变大了。这时候取模的作用真正的用上了。我们的问题转化成组合数取模而不是单单的求组合数,应用上面的方法求解不再满足要求(会超时)。
       但我们在初中都学过C(i,j) = C(i-1,j) + C(i-1,j-1).很自然的我们想到用dp的思想或者记忆化搜索的方法将0<=n,m<=1000的所有值存到数组中,每次询问直接调用。

dp代码::

#include <iostream> 
#include <sstream> 
#include <ios> 
#include <iomanip> 
#include <functional> 
#include <algorithm> 
#include <vector> 
#include <string> 
#include <list> 
#include <queue> 
#include <deque> 
#include <stack> 
#include <set> 
#include <map> 
#include <cstdio> 
#include <cstdlib> 
#include <cmath> 
#include <cstring> 
#include <climits> 
#include <cctype> 
using namespace std; 
#define XINF INT_MAX 
#define INF 0x3FFFFFFF 
#define MP(X,Y) make_pair(X,Y) 
#define PB(X) push_back(X) 
#define REP(X,N) for(int X=0;X<N;X++) 
#define REP2(X,L,R) for(int X=L;X<=R;X++) 
#define DEP(X,R,L) for(int X=R;X>=L;X--) 
#define CLR(A,X) memset(A,X,sizeof(A)) 
#define IT iterator 
typedef long long ll; 
typedef pair<int,int> PII; 
typedef vector<PII> VII; 
typedef vector<int> VI; 
const ll MOD = 1000000007; 
ll dp[1005][1005]; 

void com_num(int t,int mod)//生成1000以内的所有组合数
{
	REP2(i,0,t) { 
		REP2(j,0,i) { 
            if(j == 0||j==i)dp[i][j] = 1; 
            else dp[i][j] = (dp[i-1][j]%mod+dp[i-1][j-1]%mod)%mod; 
        } 
    }
}
int main() 
{ 
     com_num(1000,MOD);
    int T; 
    scanf("%d",&T); 
    while(T--) 
    { 
        int a,b; 
        scanf("%d%d",&a,&b); 
        printf("%lld\n",dp[a][b]); 
    } 
    return 0; 
  
}

记忆化搜索代码::

#include <sstream> 
#include <ios> 
#include <iomanip> 
#include <functional> 
#include <algorithm> 
#include <vector> 
#include <string> 
#include <list> 
#include <queue> 
#include <deque> 
#include <stack> 
#include <set> 
#include <map> 
#include <cstdio> 
#include <cstdlib> 
#include <cmath> 
#include <cstring> 
#include <climits> 
#include <cctype> 
using namespace std; 
#define XINF INT_MAX 
#define INF 0x3FFFFFFF 
#define MP(X,Y) make_pair(X,Y) 
#define PB(X) push_back(X) 
#define REP(X,N) for(int X=0;X<N;X++) 
#define REP2(X,L,R) for(int X=L;X<=R;X++) 
#define DEP(X,R,L) for(int X=R;X>=L;X--) 
#define CLR(A,X) memset(A,X,sizeof(A)) 
#define IT iterator 
typedef long long ll; 
typedef pair<int,int> PII; 
typedef vector<PII> VII; 
typedef vector<int> VI; 
const ll MOD = 1000000007; 
ll dp[1005][1005]; 

//计算C(m,n)对mod取模,每求一次就要调用一次;A,B是记忆化搜索的关键
ll com_num(int m,int n,int mod){
	if(dp[m][n]) return dp[m][n];//已经搜索过的避免重复
	if(m == n||n == 0)return 1;//终止条件
	dp[m][n] = (com_num(m-1,n-1,mod)+com_num(m-1,n,mod))%mod;
	return dp[m][n];
}
int main() 
{ 
    int T; 
    scanf("%d",&T); 
    while(T--) 
    { 
        int a,b; 
        scanf("%d%d",&a,&b); 
        printf("%lld\n",com_num(a,b,MOD));//直接输出com_num()的返回值即可 
    } 
    return 0; 
  
}

shuoj1939-组合数level2

Description

求C(n,m)对k取模后的值,已知公式如下

Input

第一行有一个整数T,表示有T组数据(T≤10)

接下来有T行,每行有三个整数n,m,k,用空格隔开。

0≤n≤10^5,0≤m≤n,1≤k≤10^9+7

Output

对于每组数据,输出答案并换行

Sample Input

2
4 2 100000000 7
5 2 1000000007

Sample Output



6
10

Source

题解::可以发现m,n从1000涨到100000。我们可以想到的一种方法通过把n,m,(n-m)分解素因子将因子存放在数组中,让n中的减掉m,(n-m)中的然后乘起来对mod取模。这是一种解题的好思路,但是会遇到两个问题::(1)分解素因子(2)把多个因子乘起来(如果说有1e5个2,1e5个3……就很有可能超时)所以我们还需要一个求快速幂的方法。这两个问题解决了就ok了。

(1)我们可以通过枚举[2 , 根号n]的方式求出n的素因子分解

(2)我们发现看k^13 = k^8*k^4*k^1用二进制表示k^(1101) = k^(1000)*k^(100)*k^(1),求快速幂的诀窍就在这里。思路代码里见或者点下面的链接:快速幂

代码::

#include <iostream>
#include <sstream>
#include <ios>
#include <iomanip>
#include <functional>
#include <algorithm>
#include <vector>
#include <string>
#include <list>
#include <queue>
#include <deque>
#include <stack>
#include <set>
#include <map>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <climits>
#include <cctype>
using namespace std;
#define XINF INT_MAX
#define INF 0x3FFFFFFF
#define MP(X,Y) make_pair(X,Y)
#define PB(X) push_back(X)
#define REP(X,N) for(int X=0;X<N;X++)
#define REP2(X,L,R) for(int X=L;X<=R;X++)
#define DEP(X,R,L) for(int X=R;X>=L;X--)
#define CLR(A,X) memset(A,X,sizeof(A))
#define IT iterator
typedef long long ll;
typedef pair<int,int> PII;
typedef vector<PII> VII;
typedef vector<int> VI;
//const int MAXN = 10010;
//#define INF 0x3FFFFFFF
//const int MOD = 1e9+7;
int a[100005];//此处数组不能小于100000
int b[100005];
int c[100005];
void fenjie1(int x){//分解竭诚
    int k;
    for(k = x;k>1;k--){
        int j = k;int i;
        for(i = 2;i*i<=j;i++){
            while(j%i==0){
                a[i]++;
                j /=i;
            }
        }
        if(j!=1)
        a[j]++;
    }
}
void fenjie2(int x){
    int k;
    for(k = x;k>1;k--){
        int j = k;int i;
        for(i = 2;i*i<=j;i++){
            while(j%i==0){
                b[i]++;
                j /=i;
            }
        }
        if(j!=1)
        b[j]++;
    }
}
void fenjie3(int x){
    int k;
    for(k = x;k>1;k--){
        int j = k;int i;
        for(i = 2;i*i<=j;i++){
            while(j%i==0){
                c[i]++;
                j /=i;
            }
        }
        if(j!=1)
        c[j]++;
    }
}
ll fast_mod(int n,int m,int k){
    ll ans = 1;
    while(m){
        if(m&1)ans = ans * n % k;
        m>>=1;
        n = n*n%k;
    }
    return ans;
}
int main(){
    int T ;
    int m,n,k;
    ll answer;
    cin>>T;
    while(T--){
        cin>>m>>n>>k;
        answer = 1;
        memset(a,0,sizeof(a));
        memset(b,0,sizeof(b));
        memset(c,0,sizeof(c));
        fenjie1(m);
        fenjie2(n);
        fenjie3(m-n);
        REP(i,100005){
            a[i] = a[i] - b[i] - c[i];
            if(a[i]!= 0)
            answer = answer * fast_mod(i,a[i],k)%k;
        }
        cout<<answer<<endl;
    }
    return 0;
}





1940: 组合数level3

Time Limit: 1 Sec   Memory Limit: 128 MB
Submit: 26   Solved: 13
[ Submit][ Status][ Web Board]

Description

求C(n,m)对k取模后的值,已知公式如下

Input

第一行有一个整数T,表示有T组数据(T≤100)

接下来有T行,每行有三个整数n,m,k,用空格隔开。

0≤n≤10^5,0≤m≤n,1≤k≤10^9+7,并且保证k为素数

Output

对于每组数据,输出答案并换行

Sample Input

2
4 2 1000000007
5 2 1000000007

Sample Output

6
10

Source


题解::由题知,组数T从10 变成100,这样会造成TLE,同时k也有了变化,从任意数变成素数。这是就要转换思路求解,我们知道通过费马小定理知道:


a*x =1 mod k 

>>

 x = a ^ -1 mod k 

>>

 (当k为素数时,a^k mod k = a mod k:费马小定理)

>>

a^(k-1)  modk = 1 mod k

>>

x = a^-1 mod k = a^(k-2) mod k  (a的逆元)

所以设inv(a,k)为求a在mod为k时的逆元

C(n,m) = fac[n]/(fan[m]*fan[n-m] mod k) = fac[n] * inv (fan[m]*fan[n-m] mod k, k) mod k;

当且仅当gcd(a,k) == 1时逆元存在

#include <iostream>
#include <sstream>
#include <ios>
#include <iomanip>
#include <functional>
#include <algorithm>
#include <vector>
#include <string>
#include <list>
#include <queue>
#include <deque>
#include <stack>
#include <set>
#include <map>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <climits>
#include <cctype>
using namespace std;
#define XINF INT_MAX
#define INF 0x3FFFFFFF
#define MP(X,Y) make_pair(X,Y)
#define PB(X) push_back(X)
#define REP(X,N) for(int X=0;X<N;X++)
#define REP2(X,L,R) for(int X=L;X<=R;X++)
#define DEP(X,R,L) for(int X=R;X>=L;X--)
#define CLR(A,X) memset(A,X,sizeof(A))
#define IT iterator
typedef long long ll;
typedef pair<int,int> PII;
typedef vector<PII> VII;
typedef vector<int> VI;

ll fac[100005];
void qfac(int n,int k){
    fac[1] = 1;
    REP2(i,2,n)
    fac[i] = fac[i-1]*i%k;
}
ll fast_mod(ll n,int m,int k){
    ll ans = 1;
    while(m){
        if(m&1) ans = ans*n%k;
        m>>=1;
        n = n*n%k;
    }
    return ans;
}
ll inv(ll n,int k){
    ll ans = fast_mod(n,k-2,k);
    return ans;
}

int main(){
    int m,n,k;
    int T;
    cin>>T;
    while(T--){
        cin>>m>>n>>k;
        memset(fac,0,sizeof(fac));
        qfac(m,k);
        //REP(i,10)cout<<fac[i]<<endl;
        cout<<fac[m]*inv(fac[n],k)%k*inv(fac[m-n],k)%k<<endl;
    }
    return 0;
}



6
10

Source

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值