组合数取模问题
加法递推
由 \(\displaystyle \binom{n}{m}=\binom{n-1}{m}+\binom{n-1}{m-1}\) 求出
边界条件: \(\displaystyle \binom{1}{0}=\binom{1}{1}=1\) 和 \(\displaystyle \binom{n}{0}=1\)
时间复杂度: \(O(n^2)\)
const int MOD = 1000000007;
void init(int n)
{
c[1][0] = c[1][1] = 1;
for(int i = 2; i <= n; i++) {
c[i][0] = 1;
for(int j = 1; j <= i; j++)
c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % MOD;
}
}
乘法递推
由 \(\displaystyle\binom{n}{m}=\frac{n-m+1}{m}\binom{n}{m-1}\) 求出
边界条件: \(\displaystyle\binom{n}{0}=1\)
时间复杂度: \(O(n)\)
const int MOD = 1000000007;
ll qpow(ll a, ll b)
{
ll res = 1;
for(; b; b >>= 1) {
if(b & 1)
res = res * a % MOD;
a = a * a % MOD;
}
return res;
}
ll inv(ll a)
{
return qpow(a, MOD - 2);
}
void init(int n)
{
c[0] = 1;
for(int i = 2; i * 2 <= n; i++)
c[i] = c[n - i] = 1ll * (n - i + 1) * c[i - 1] % MOD * inv(i) % MOD;
}
阶乘逆元
接下来考虑如何求阶乘的逆元,设 \(f_i=i!\)
上式就是阶乘逆元的递推式,但在递推式求解之前,我们必须先预处理出 \((i)^{-1}\)
令 \(k=\lfloor\frac{p}{i}\rfloor ,j=p \bmod i\) ,有 \(p=ki+j\) ,则 \(ki+j\equiv 0\pmod{p}\)
最后一步把 \(k\) 换成 \((p-\lfloor\frac{p}{i}\rfloor)\) 而非 \(\lfloor\frac{p}{i}\rfloor\) 的目的是为了避免出现负数,这样我们就得到了求 \(1\sim n\) 的逆元的递推式:
综上,我们先递推处理出 \(1\sim n\) 的逆元,进而求出 \(1!,2!\cdots n!\) 的逆元,对于每次询问,只需要带入第一个公式即可。预处理复杂度 \(O(n)\) ,单次询问复杂度 \(O(1)\)
const int MOD = 1000000007;
const int MAX_N = 1000000 + 5;
int inv[MAX_N];
int fact[MAX_N], invf[MAX_N];
void init(int n)
{
inv[1] = 1;
for(int i = 2; i <= n; i++)
inv[i] = 1ll * (MOD - MOD / i) * inv[MOD % i] % MOD;
fact[0] = invf[0] = 1;
for(int i = 1; i <= n; i++) {
fact[i] = 1ll * fact[i - 1] * i % MOD;
invf[i] = 1ll * invf[i - 1] * inv[i] % MOD;
}
}
int C(int n, int m)
{
if(n < m)
return 0;
return 1ll * fact[n] * invf[m] % MOD * invf[n - m] % MOD;
}
Lucas定理
定理
等价形式
我们将 \(n,m\) 表示成 \(p\) 进制数的形式:
则有:
证明等价十分容易:在之前的公式中,对 \(p\) 取模相当于取了 \(p\) 进制下的最低位,除以 \(p\) 后向下取整相当于在 \(p\) 进制下去掉最低位,不断重复这一过程,就能发现两个形式的等价
证明
由 \(p\) 为素数可知, \(\forall j,1\leq j \leq p-1\) 都有:
所以
用这个结论可以推出:
\(\displaystyle \binom{n}{m}\) 就是 \((1+x)^n\) 的展开式在 \(x^m\) 处的二项式系数
- \((1+x^p)^{\lfloor n/p\rfloor}\) 展开后得到的项次数均为 \(p\) 的倍数
- \((1+x)^{n\bmod p}\) 展开后得到的项次数小于等于 \(p-1\)
综上, \(x^m\) 只可能由一种方案产生,\(m=p\lfloor\frac{m}{p}\rfloor+m\bmod p\) ,所以在 \((1+x^p)^{\lfloor n/p\rfloor}\) 中取系数 \(\displaystyle \binom{\lfloor n/p\rfloor}{\lfloor m/p\rfloor}\) ,在 \((1+x)^{n\bmod p}\) 中取 \(\displaystyle \binom{n \bmod p}{m\bmod p}\)
可以得出结论:
代码
void lucas(int n, int m, int p)
{
if(m == 0)
return 1;
return 1ll * lucas(n / p, m / p, p) * C(n % p, m % p) % p;
}
用阶乘逆元法预处理组合数的复杂度为 \(O(p)\) ,单次询问的复杂度为 \(O(\log p)\)
推论
由卢卡斯定理的等价形式可知,只要存在 \(n_i=0,m_i=1\) ,则 \(\displaystyle\binom{n_i}{m_i}=0\) ,进一步得出 \(\displaystyle\binom{n}{m}\equiv 0\pmod{2}\)
所以当 \(\displaystyle\binom{n}{m}\) 为奇数时,若 \(m_i=1\) 则一定有 \(n_i=1\) ,所以对于任意 \(i\) ,都有 \(n_i\&m_i=m_i\) ,即 \(n\&m=m\)
exLucas定理
待更