【模板】盧卡斯與拓展盧卡斯
阿新 • • 發佈:2018-12-20
LUCAS
EXLUCAS
設模數為為互不相同的質數。 首先分別求出。 問題便轉換成了求解:。套用CRT解決。
那麼問題在於如何快速求出所有的。
當時,套用LUCAS。
當時,無法對質數的次冪快速取模,考慮分別對進行拆分取模,最終答案即為(表示在模意義下的逆元,exgcd求解)。
拆分取模方式如下:
假設當前,求解。
對於第一部分,等價於,剩餘的數必然少於個,而將右邊的遞迴下去求解即可。
程式碼
#include<bits/stdc++.h>
#define debug(x) cerr<<#x<<":"<<x<<endl
using namespace std;
typedef long long ll;
const int N=5010;
ll n,m,p,q[N],a[N];
int tot;
namespace exlucas{
inline int fp(ll x,ll y,ll p)
{
ll re=1;
for(;y;y>>=1,x=x*x%p)
if(y&1) re=re*x%p;
return re;
}
ll exgcd(ll n,ll m,ll &x,ll &y)
{
if(!m)
{x=1;y=0;return n;}
ll re=exgcd(m,n%m,y,x);y-=(n/m)*x;
return re;
}
inline ll inv(ll n,ll p)
{
ll x,y;
exgcd(n,p,x,y);
return (x%p+p)%p;
}
inline ll C(ll n,ll m,ll p)
{
if(m>n) return 0;
ll re=1,x,y,z,i;
for(i=1;i<=m;++i){
x=n-i+1;y=(int)inv(i,p);
re=re*x%p*y%p;
}
return re;
}
ll lucas(ll n,ll m,ll p)
{
if(m==0 || n==m) return 1;
return lucas(n/p,m/p,p)*C(n%p,m%p,p)%p;
}
ll cal(ll n,ll a,ll b,ll p)
{
if(!n) return 1;
ll i,j,re=1;
for(i=1;i<p;++i) if(i%a) re=re*i%p;
re=fp(re,n/p,p);
for(i=n/p*p+1;i<=n;++i) if(i%a) re=re*i%p;
return re*cal(n/a,a,b,p)%p;
}
inline ll multilucas(ll n,ll m,ll a,ll b,ll p)
{
ll i,s=0;
for(i=n;i;i/=a) s+=i/a;
for(i=m;i;i/=a) s-=i/a;
for(i=n-m;i;i/=a) s-=i/a;
return fp(a,s,p)*cal(n,a,b,p)%p*inv(cal(m,a,b,p),p)%p*inv(cal(n-m,a,b,p),p)%p;
}
inline ll cal(ll n,ll m,ll p)
{
ll i,j,b,c,d,ans,mod,x,y;
for(i=2;i*i<=p;++i)
if(p%i==0){
q[++tot]=1;
for(j=0;p%i==0;p/=i) q[tot]*=i,j++;
if(j==1) a[tot]=lucas(n,m,i);
else a[tot]=multilucas(n,m,i,j,q[tot]);
}
if(p>1){q[++tot]=p;a[tot]=lucas(n,m,p);}
ans=a[1];mod=q[1];
for(i=2;i<=tot;++i){
b=exgcd(mod,q[i],x,y);
c=a[i]-ans;
if(c%b) return -1LL;
d=q[i]/b;x=((c/b)%d*x%d+d)%d;
ans=ans+mod*x;
mod=mod/b*q[i];
}
return ans;
}
}
int main(){
scanf("%lld%lld%lld",&n,&m,&p);
printf("%lld\n",exlucas::cal(n,m,p));
return 0;
}