卢卡斯定理

卢卡斯定理

\[\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);
}
posted @ 2020-09-16 22:11  lhm_liu  阅读(301)  评论(0编辑  收藏  举报