题解 P4735 【【模板】扩展卢卡斯】
Great_Influence · · 题解
扩展卢卡斯定理。
首先,得确定先会扩展欧几里得算法和扩展中国剩余定理。至于卢卡斯定理,那并不重要。
设
假如你已经求出了
那么问题转换为如何求出
可以知道:
那么问题即转化为求出几个阶乘和阶乘的逆元。
现在,问题归为如何快速求出阶乘。
为了便于统计出现了多少个
那么接下来只剩下非
为了更好地理解上方几条,可以举个栗子:
当
这样就可以在可承受的时间复杂度内求出阶乘。但是,为了达到除法的效果,我们需要考虑
边界为:
开始计算时间复杂度。
首先是中国剩余定理的复杂度,可以简单查到是
然后是求阶乘复杂度。当求
最后是求逆元的复杂度。利用扩展欧几里得可以做到
因此,总复杂度为
这个复杂度和
ext:
如果要同时计算多个组合数膜同一个合数时,可以选择对每个质因子
代码(ext部分没有贴代码):
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll exgcd(ll a,ll b,ll &x,ll &y)
{
if(!b){x=1;y=0;return a;}
ll res=exgcd(b,a%b,x,y),t;
t=x;x=y;y=t-a/b*y;
return res;
}
ll p;
inline ll power(ll a,ll b,ll mod)
{
ll sm;
for(sm=1;b;b>>=1,a=a*a%mod)if(b&1)
sm=sm*a%mod;
return sm;
}
ll fac(ll n,ll pi,ll pk)
{
if(!n)return 1;
ll res=1;
for(register ll i=2;i<=pk;++i)
if(i%pi)(res*=i)%=pk;
res=power(res,n/pk,pk);
for(register ll i=2;i<=n%pk;++i)
if(i%pi)(res*=i)%=pk;
return res*fac(n/pi,pi,pk)%pk;
}
inline ll inv(ll n,ll mod)
{
ll x,y;
exgcd(n,mod,x,y);
return (x+=mod)>mod?x-mod:x;
}
inline ll CRT(ll b,ll mod){return b*inv(p/mod,mod)%p*(p/mod)%p;}
const int MAXN=11;
static ll n,m;
static ll w[MAXN];
inline 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 k=0;
for(register ll i=n;i;i/=pi)k+=i/pi;
for(register ll i=m;i;i/=pi)k-=i/pi;
for(register ll i=n-m;i;i/=pi)k-=i/pi;
return up*inv(d1,pk)%pk*inv(d2,pk)%pk*power(pi,k,pk)%pk;
}
inline ll exlucus(ll n,ll m)
{
ll res=0,tmp=p,pk;
static int lim=sqrt(p)+5;
for(register int i=2;i<=lim;++i)if(tmp%i==0)
{
pk=1;while(tmp%i==0)pk*=i,tmp/=i;
(res+=CRT(C(n,m,i,pk),pk))%=p;
}
if(tmp>1)(res+=CRT(C(n,m,tmp,tmp),tmp))%=p;
return res;
}
int main()
{
scanf("%lld%lld%d",&n,&m,&p);
printf("%d\n",exlucus(n,m));
return 0;
}