计算组合数的几种方法

  1. 数据比较小,直接采用C(a, b) = a * (a - 1) *....* (a - b + 1) / (b * (b - 1) *...* 2 * 1)
    试用数据范围:a <= 29。在a = 30, b = 15时,计算分子乘机时即超范围
    LL C(LL n,LL m)
    {
    	if(n<m)
    		return 0;
    	LL tmp=1;
    	for(int i=n,i>=n-m+1;i--)
    		tmp*=i;
    	for(int i=b;i>=1;i--)
    		tmp/=i;
    	return tmp;
    }
  2. 预处理出需要的组合数,如需计算较大的组合数可采用(经常会取模,也很方便)。使用C(a, b) = C(a - 1, b - 1) + C(a - 1, b - 1)递推处理
    因为计算过程中采用递推的加法运算,所以不取模的时候最大可以算到a = 66
    但是,这种情况一般伴随着取模的操作,所以考虑到内存限制的时候,一般可以计算到a = 1000(不一定,受限于内存)
    const int maxn1=1000;
    const int maxn2=1000;
    const long long mod=1000000007;
    LL f[maxn1][maxn2];
    void C()
    {
    	for(int i=0;i<=maxn1;i++)
    		f[i][0]=1;
    	for(int i=1;i<=maxn1;i++)
    	{
    		for(int j=1;j<=min(i,maxn2-1);j++)
    		{
    			f[i][j]=(f[i-1][j]+f[i-1][j-1])%mod;
    		}
    	}
    }
  3. 采用分解质因子的方式,可以计算足够大的数(因为数字会超过long long的范围,所以结果依然用质因子表示,模板中计算出了相应的数)
        map <int, LL> m;  
          
        //分解质因数  
        //k为1或-1  
        void fun(int n, int k)  
        {  
            for (int i = 2; i <= sqrt(n * 1.0); i++)  
            {  
                while (n % i == 0)  
                {  
                    n /= i;  
                    m[i] += k;  
                }  
            }  
            if (n > 1)  
            {  
                m[n] += k;  
            }  
        }  
          
        //大数快速幂取模  
        LL quick_pow(LL a, LL b)  
        {  
            LL ret = 1;  
            while (b)  
            {  
                if (b & 1)  
                {  
                    ret *= a;  
                    ret %= MOD;  
                }  
                b >>= 1;  
                a *= a;  
                a %= MOD;  
            }  
            return ret;  
        }  
          
        //求组合数  
        LL C(LL a, LL b)  
        {  
            if (a < b || a < 0 || b < 0)  
                return 0;  
            m.clear();  
            LL ret = 1;  
            b = min(a - b, b);  
            for (int i = 0; i < b; i++)  
            {  
                fun(a - i, 1);  
            }  
            for (int i = b; i >= 1; i--)  
            {  
                fun(i, -1);  
            }  
          
            ///以下计算出了具体的数  
            for (__typeof(m.begin()) it = m.begin(); it != m.end(); it++)  
            {  
                if ((*it).second != 0)  
                {  
                    ret *= quick_pow((*it).first, (*it).second);  
                    ret %= MOD;  
                }  
            }  
            return ret;  
        }  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值