Lucas定理
卢卡斯定理就是用来算组合数(二项式系数)\(\left(\begin{matrix} n\\m \end{matrix}\right)\)在模质数意义下的答案的.
内容大致如下:
\[\left(\begin{matrix} n\\m \end{matrix}\right)=\left(\begin{matrix} k_1p+b_1\\k_2p+b_2 \end{matrix}\right)=\left(\begin{matrix} k_1\\k_2 \end{matrix}\right) * \left(\begin{matrix} b_1\\b_2 \end{matrix}\right)\]
简要证明:
\[G(x)=(1+x)^n=(1+x)^{k_1p}(1+x)^{b_1}\\ =(\sum_{i=0}^{k_1}C^{i}_{k_1}x^{ip})*(\sum_{i=0}^{b_1}C^i_{b_1}x^{i})\]
由于p是质数,发现要得到第m项,两个括号里唯一的两项相乘得到这两项分别是\(C_{k_1}^{k_2}x^{k_2p}\)和\(C_{b_1}^{b_2}x^{b_2}\)
于是我们得到母函数m次项的系数\[C_n^m=C_{k_1}^{k_2}C_{b_1}^{b_2}\]
所以得证.
代码:
ll lucas(int x,int y)
{
if(x
扩展Lucas
参考【模板】扩展卢卡斯 题解 作者: Great_Influence
lucas定理在大多数情况下都用不了,因为它要求p必须要是质数.当p不是质数时就用不了了.
但是我们知道仍和一个数都可以唯一表示成k个质数相乘,那我们对这k个质因子做lucas,再用中国剩余定理合并就好了.
ll exlucas(ll n,ll m)
{
ll res=0,t=p,p2,sz=sqrt(p)+1;
for(int i=2;i<=sz;i++)
{
if(t%i)continue;p2=1;
while(t%i==0)t/=i,p2*=i;
res=(res+crt(calc(n,m,i,p2),p2))%p;
}
if(t>1)res+=(crt(calc(n,m,t,t),t)),res%=p;
return res;
}
这样合并答案就没问题了,但是如果把n,m的范围再加大到\(10^{18}\),那上述方法又不行了.
你可能会想,怎么不可以,不是都是质因子吗,它们都可以用lucas直接求出答案啊.
但是有可能一个质因子出现了多次比如\(24=3*2*2*2\),我们构造一个数23,你现在知道四个同余方程:\[ x\equiv 2(\mod 3) \]
\[ x\equiv1(\mod 2) \]
\[ x\equiv1(\mod 2) \]
\[ x\equiv1(\mod 2) \]
那么怎么可能的解就有\(5,11,17,13\)都是满足的,但是你又不能对于相同的质因子乘起来当做一个数处理,因为相同质因子相乘得到的数也是合数.这就很棘手了.
于是换一种思路,直接用组合数的阶乘的公式来算,暴力算阶乘,但是因为要算的阶乘有可能非常非常大,复杂度接受不了.于是现在的问题的关键就是如何快速求一个大数在模一个数意义下的阶乘.即:\(\left(\begin{matrix} n\\m \end{matrix}\right)(\mod p_i^{k_i})\)
这便是扩展lucas解决的问题.
流程大致如下:
当前找到的质因子为p=3,它出现了k=2次,然后n的大小为19,那么我们先把阶乘写成这样.
\[ (1*2*4*5*7*8*10*11*13*14*16*17*19)*(3*6*9*12*15*18) \\(1*2*4*5*7*8*10*11*13*14*16*17*19)*(1*2*3*4*5*6)*3^6\\ (1*2*4*5*7*8)^2*3^6*19*(1*2*3*4*5*6)*3^6 \]
而算阶乘的流程差不多可以概括成,找循环节,算出循环节剔除掉含因子p的数乘积,对他取循环次数次幂.
还会有剩余的不属于仍和循环的余项,暴力求解即可.
再把刚刚剔除的含因子p的数提出一个因子p,剩下的一定是一个阶乘,递归求解这个阶乘.
注意到上面例子中没有处理两个\(3^6\),这是因为这个东西如果乘到阶乘里面,阶乘就可能不会有逆元了,因为它和我们取模的数并不是互质的.所以放在外面单独处理.
由于\(n!\)一定可以除尽\((n-m)!m!\)那么含这个因子的个数前者也一定大于后者.直接统计这个因子的个数\(n!\)加\(m!\)减\((n-m)!\)减就可以了,再直接用快速幂算就可以了.
ll fac(ll x,ll p1,ll p2)//算阶乘mod p2
{
if(!x)return 1;ll res=1;
for(ll i=2;i<=p2;i++)
if(i%p1)(res*=i)%=p2;//单个循环节的乘积
res=ksm(res,x/p2,p2);//乘上 循环节 的 循环次数 次方
for(ll i=2;i<=x%p2;i++)
if(i%p1)(res*=i)%=p2;//乘上剩余的项
return res*fac(x/p1,p1,p2)%p2;//把断开循环节的元素(整除p1)除掉p1变为阶乘,重新递归
}
ll calc(ll n,ll m,ll p1,ll p2)
{
ll up=fac(n,p1,p2),d1=fac(m,p1,p2),d2=fac(n-m,p1,p2),sum=0;
for(ll i=n ;i;i/=p1)sum+=i/p1;
for(ll i=m ;i;i/=p1)sum-=i/p1;
for(ll i=n-m;i;i/=p1)sum-=i/p1;
return up*inv(d1,p2)%p2*inv(d2,p2)%p2*ksm(p1,sum,p2)%p2;
}