Lucas
卢卡斯定理:
( m n ) = ( m p n p ) ∗ ( m ( m o d p ) n ( m o d p ) ) ( m o d p ) (^n_m)=(^{\frac{n}{p}}_{\frac{m}{p}}) * (^{n \pmod p}_{m \pmod {p}})\pmod p (mn)=(pmpn)∗(m(modp)n(modp))(modp)
后面部分可以递归,就是n和m在p进制下每一位的对应的组合数
证明要二项式定理
条件是P是质数
代码:
ll C(ll s,ll t){
if(s>t) return 0;
if(s==t) return 1;
return (f[t]*g[s])%p*g[t-s]%p;
}
ll lucas(ll s,ll t){
if(s==0) return 1;
pp=s%p;qq=t%p;
//printf("%lld %lld\n",s,t);
return C(pp,qq)*lucas(s/p,t/p)%p; //C处理出0~p-1的阶乘与其逆元就好
}
复杂度大概是 O log p O \log p Ologp?
EXlucas
处理p不为质数,考虑把p分解为
p = ∏ i − 1 k p i e i p=\prod^{k}_{i-1}p_i^{e_i} p=i−1∏kpiei
然后对于每个质数的乘积求出 ( m n ) ( m o d p i e i ) (^n_m)\pmod {p_i^{e_i}} (mn)(modpiei),用CRT合并
每个质因子乘积的
(
m
n
)
(
m
o
d
p
i
e
i
)
(^n_m)\pmod {p_i^{e_i}}
(mn)(modpiei)为
n
!
m
!
∗
(
n
−
m
)
!
(
m
o
d
p
i
e
i
)
\frac{n!}{m!*(n-m)!} \pmod {p_i^{e_i}}
m!∗(n−m)!n!(modpiei)
考虑对于 x ! ( m o d p i e i ) x! \pmod {p_i^{e_i}} x!(modpiei),有
x ! = ∏ i = 1 , i ( m o d p ) ! = 0 p k i ( m o d p ) x p ∗ ∏ i = x p + 1 x i ( m o d p ) ∗ p n p ∗ x p ! x! = {\prod^{p^k}_{i=1,i\pmod p !=0} i\pmod p}^{\frac{x}{p}}*\prod_{i=\frac{x}{p}+1}^x i\pmod p *{p^{\frac{n}{p}}} *\frac{x}{p} ! x!=i=1,i(modp)!=0∏pki(modp)px∗i=px+1∏xi(modp)∗ppn∗px!
举个例子,
当
n
=
19
,
p
=
3
,
k
=
2
n=19,p=3,k=2
n=19,p=3,k=2时:
n
!
=
1
∗
2
∗
3
∗
⋯
∗
19
n!=1*2*3*\cdots*19
n!=1∗2∗3∗⋯∗19
=
(
1
∗
2
∗
4
∗
5
∗
7
∗
8
∗
10
∗
11
∗
13
∗
14
∗
16
∗
17
∗
19
)
∗
3
6
∗
6
!
=(1*2*4*5*7*8*10*11*13*14*16*17*19)*3^{6}*6!
=(1∗2∗4∗5∗7∗8∗10∗11∗13∗14∗16∗17∗19)∗36∗6!
≡
(
1
∗
2
∗
4
∗
5
∗
7
∗
8
)
2
∗
19
∗
3
6
∗
6
!
\equiv(1*2*4*5*7*8)^2*19*3^6*6!
≡(1∗2∗4∗5∗7∗8)2∗19∗36∗6!
然后发现x必须先要和p互质,
于是上面
p
n
p
p^{\frac{n}{p}}
ppn就不算了
然后最后在外面补一个
复杂度PlogP的
看代码:
#include<bits/stdc++.h>
#define rep(i,a,b) for(register int i=(a);i<=(b);i++)
#define ll long long
ll test[15]={0,2,61,23,37};
inline ll read(){
ll x=0;int pos=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') pos=0 ;
for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';
return pos?x:-x;
}
ll n,m,p;
void exgcd(ll a,ll b,ll &x,ll &y){
if(!b) x=1,y=0;
else{
exgcd(b,a%b,y,x);
y-=x*(a/b);
}
}
ll ksm(ll aa,ll bb,ll mod){
ll nans=1;
while(bb){
if(bb&1) nans=(nans*aa)%mod;
aa=(aa*aa)%mod;
bb>>=1;
}
return nans;
}
ll inv(ll xx,ll mod){
ll x,y;exgcd(xx,mod,x,y);
return (x%mod+mod)%mod;
}
ll fac(ll xx,ll pi,ll pk){
if(!xx) return 1;
ll res=1;
for(ll i=2;i<=pk;++i){
if(i%pi!=0) res=(res*i)%pk;
}
res=ksm(res,xx/pk,pk);
for(ll i=2;i<=xx%pk;++i){
if(i%pi!=0) res=(res*i)%pk;
}
return res*fac(xx/pi,pi,pk)%pk;
}
ll C(ll n,ll m,ll pi,ll pk){
ll up=fac(n,pi,pk),d1=fac(m,pi,pk),d2=fac(n-m,pi,pk);
ll resp=0;
for(ll i=n;i;i/=pi) resp+=i/pi;
for(ll i=m;i;i/=pi) resp-=i/pi;
for(ll i=n-m;i;i/=pi) resp-=i/pi;
return up*inv(d1,pk)%pk*inv(d2,pk)*ksm(pi,resp,pk)%pk;
}
ll CRT(ll b,ll mod){
return b*inv(p/mod,mod)%p*(p/mod)%p;
}
ll out(ll aa){
if(aa>9) out(aa/10);
putchar(aa%10+'0');
}
ll work(ll n,ll m){
ll res=0,tmp=p,pk=1;
int len=sqrt(p);
for(register int i=2;i<=len;++i){
if(tmp%i==0){
pk=1;while(tmp%i==0) pk*=i,tmp/=i;
int pp=i;
res=(res+CRT(C(n,m,pp,pk),pk))%p;
}
}
if(tmp>1){
int pp=tmp;pk=tmp;
res=(res+CRT(C(n,m,pp,pk),pk))%p;
}
return res;
}
int main(){
n=read();m=read();p=read();
out(work(n,m));
return 0;
}
例题
基本裸的,不难看出组合数的方案,重点在于取模
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cmath>
#define rep(i,a,b) for(long long i=(a);i<=(b);++i)
#define per(i,a,b) for(long long i=(a);i>=(b);--i)
#define ll long long
long long read(){
long long x=0,pos=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') pos=0;
for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';
return pos?x:-x;
}
using namespace std;
ll n,m,mod,w[10];
ll ksm(ll a,ll b,ll mo){
ll na=1;
while(b){
if(b&1){
na=(na*a)%mo;
}
a=(a*a)%mo;
b>>=1;
}
return na;
}
ll fac(ll n,ll pi,ll pk){
if(!n) return 1;
ll res=1;
rep(i,2,pk) if(i%pi) res=(res*i)%pk;
res=ksm(res,n/pk,pk);
rep(i,2,n%pk) if(i%pi) res=(res*i)%pk;
return res*fac(n/pi,pi,pk)%pk;
}
void exgcd(ll a,ll b,ll &x,ll &y){
if(b==0) x=1,y=0;
else exgcd(b,a%b,y,x),y-=x*(a/b);
}
ll inv(ll now,ll mo){
ll x,y;
exgcd(now,mo,x,y);
return (x%mo+mo)%mo;
}
ll C(ll n,ll m,ll pi,ll pk){
ll up=fac(n,pi,pk),d1=fac(m,pi,pk),d2=fac(n-m,pi,pk);
ll resp=0;
for(int i=n;i;i/=pi) resp+=i/pi;
for(int i=m;i;i/=pi) resp-=i/pi;
for(int i=n-m;i;i/=pi) resp-=i/pi;
return up*inv(d1,pk)%pk*inv(d2,pk)%pk*ksm(pi,resp,pk)%pk;
}
ll CRT(ll a,ll b){
return a*inv(mod/b,b)%mod*(mod/b)%mod;
}
ll exlucas(ll n,ll m,ll p){
ll pk=1,res=0,tmp=p;
int len=sqrt(p);
rep(i,2,len){
if(tmp%i==0){
pk=1;
while(tmp%i==0) tmp/=i,pk*=i;
ll Ci=C(n,m,i,pk);
res=(res+CRT(Ci,pk))%p;
}
}
if(tmp>1){
ll Ci=C(n,m,tmp,tmp);
res=(res+CRT(Ci,tmp))%p;
}
return res;
}
int main(){
mod=read();
n=read();m=read();
rep(i,1,m){
w[i]=read();
}
ll ans=1,tot=0;
rep(i,1,m){
if(n<tot+w[i]){
printf("Impossible");
return 0;
}
ans=ans*(exlucas(n-tot,w[i],mod))%mod;
tot+=w[i];
}
printf("%lld",ans);
return 0;
}
本文详细介绍了卢卡斯定理及其扩展版本EXlucas的原理与应用,包括组合数的模运算处理方法,尤其针对质数与非质数情况下的不同处理方式,并附带示例代码。
6599

被折叠的 条评论
为什么被折叠?



