卢卡斯定理
卢卡斯定理
\[\large\binom{n}{m}\equiv\binom{n \bmod p}{m\bmod p}\binom{\frac{n}{p}}{\frac{m}{p}} \pmod{p}
\]
其中 \(p\) 为质数。即将 \(n,m\) 用 \(p\) 进制来表示。预处理阶乘后复杂度为 \(O(\log_p n)\)。
扩展卢卡斯定理
用来解决 \(p\) 不是质数的情况。设 \(p=\prod p_i^{k_i}\),得:
\[\large\begin{cases}
x\equiv \binom{n}{m} &\pmod{p_1^{k_1}} \\
x\equiv \binom{n}{m} &\pmod{p_2^{k_2}} \\
&\cdots \\
x\equiv \binom{n}{m} &\pmod{p_m^{k_m}} \\
\end{cases}
\]
求值后用中国剩余定理合并即可。
因为 \(p\) 不一定为质数,所以不能直接求逆元,考虑进行转化:
\[\large\begin{aligned}
&\binom{n}{m} \pmod{p^k}\\
=& \frac{n!}{m!(n-m)!} \pmod{p^k}\\
=& \frac{\frac{n!}{p^x}}{\frac{m!}{p^y}\frac{(n-m)!}{p^z}}p^{x-y-z} \pmod{p^k}\\
\end{aligned}
\]
其中 \(x,y,z\) 都是对应的数质因数分解后 \(p\) 的指数。
先考虑如何求 \(p^{x-y-z}\),设 \(g(n)\) 为 \(n!\) 质因数分解后 \(p\) 的指数,因为有:
\[\large n!=p^{\left\lfloor \frac{n}{p} \right\rfloor}\left(\left\lfloor \frac{n}{p} \right\rfloor\right)!\prod_{i=1,p\not\mid i}^n i
\]
所以得:
\[\large g(n)=\left\lfloor \frac{n}{p} \right\rfloor+g\left(\left\lfloor \frac{n}{p} \right\rfloor\right)
\]
再考虑如何求 \(\frac{n!}{p^x} \bmod p^k\),设 \(f(n)=\frac{n!}{p^x}\),得:
\[\large\begin{aligned}
&f(n) \pmod{p^k}\\
=&\frac{p^{\left\lfloor \frac{n}{p} \right \rfloor}}{p^x}\left(\left\lfloor \frac{n}{p} \right \rfloor\right)!\prod_{i=1,p\not\mid i}^n i \pmod{p^k}\\
=&f\left(\left\lfloor \frac{n}{p} \right \rfloor\right)\left( \prod_{i=1,p\not\mid i}^{p^k} i\right)^{\left\lfloor \frac{n}{p^k} \right \rfloor} \prod_{i=1,p\not\mid i}^{n \bmod p^k} i \pmod{p^k}\\
\end{aligned}
\]
边界为 \(f(0)=1\)。
预处理复杂度为 \(O(\sqrt p + \sum p_i^{k_i})\),询问一次复杂度为 \(O(\log_p n\log n)\)。
ll f(ll n,int x)
{
if(!n) return 1;
ll k=pk[x];
return f(n/pri[x],x)*qp(a[x][k],n/k,k)%k*a[x][n%k]%k;
}
ll C(ll n,ll m,int x)
{
ll v=0,p=pri[x],k=pk[x];
for(ll i=n;i;i/=p) v+=i/p;
for(ll i=m;i;i/=p) v-=i/p;
for(ll i=n-m;i;i/=p) v-=i/p;
return f(n,x)*inv(f(m,x),k)%k*inv(f(n-m,x),k)%k*qp(p,v,k)%k;
}
ll crt(ll x,ll m,ll p)
{
return x*(m/p)*inv(m/p,p);
}
ll exlucas(ll n,ll m)
{
ll v=0;
for(int i=1;i<=cnt;++i) v=(v+crt(C(n,m,i),p,pk[i]))%p;
return v;
}
void init(int x)
{
a[x][0]=1;
for(int i=1;i<=pk[x];++i)
{
a[x][i]=a[x][i-1];
if(i%pri[x]) a[x][i]=a[x][i]*i%pk[x];
}
}
void pre(ll x)
{
for(int i=2;i*i<=x;++i)
{
if(x%i) continue;
ll v=1;
while(x%i==0) x/=i,v*=i;
pri[++cnt]=i,pk[cnt]=v;
}
if(x!=1) pri[++cnt]=x,pk[cnt]=x;
for(int i=1;i<=cnt;++i) init(i);
}