acmでよく見られる組合せ数の計算方法のまとめについて

2289 ワード

  • 最も簡単な場合、データは比較的に小さく、直接C(a,b)=a*(a-1)*....*を採用する.(a - b + 1)/(b * (b - 1) *...* 2*1)試用データ範囲:a<=29.a=30,b=15において、分子乗機を計算するとき、すなわち超範囲
    LL C(LL a, LL b)
    {
        if (a < b) return 0;
        LL ret = 1;
        FE(i, a - b + 1, a)
            ret *= i;
        FE(i, 2, b)
            ret /= i;
        return ret;
    }
  • も比較的簡単な場合に適用され、データは比較的小さく、C(a,b)=a!/(b! * (a - b)!) 分子は第一の方法よりも大きいので,適用範囲はa<=20
    LL fx[MAXN];
    
    void init()
    {
        fx[0] = 1;
        FF(i, 1, MAXN)
            fx[i] = fx[i - 1] * i;
    }
    
    LL C(LL a, LL b)
    {
        if (a < b) return 0;
        return fx[a] / fx[b] / fx[a - b];
    }
  • と小さくなった.
  • は、必要な組合せ数を前処理し、大きな組合せ数を計算する必要がある場合に採用することができる(よく型を取ることができ、便利である).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;
    LL f[MAXN1][MAXN2];
    
    void init()
    {
        FF(i, 0, MAXN1)
            f[i][0] = 1;
        FF(i, 1, MAXN1)
        {
            FE(j, 1, min(i, MAXN2 - 1))
                f[i][j] = (f[i - 1][j] + f[i - 1][j - 1]) % MOD;
        }
    }
  • と算出できるのが一般的である.
  • 質量因子を分解することにより、十分な数(long longの範囲を超えるため、結果は質量因子で表され、テンプレートでは対応する数が算出される)
    map  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;
    }
  • を計算することができる.