題意:現在你在1層樓,每次移動能向上移動a層或者向下移動b層,問最少移動多少次能到達第m層,設樓層有足夠高並且地下層數也足夠多,因此不必考慮到達樓層上限或下限。數據範圍:1<=m,a,b<=1e9,答案對p取模,1<=p<=1e6。
題解:構造不定方程ax + by = m – 1,求解一個正數x和負數y,用擴展歐幾里得算法求解得到x-y的最小正整數解,然後輸出C(x-y, x)即可。因爲a b較大,而p較小,要使用盧卡斯定理優化。
代碼如下:
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+5;
int fac[maxn];
int inv[maxn];
int facinv[maxn];
long long mod;
long long getinv(long long x)
{
if(x==1) return 1;
return getinv(mod%x)*(mod-mod/x)%mod;
}
long long init()
{
for(int i=1;i<mod;i++)
inv[i]=getinv(i);
fac[0]=facinv[0]=1;
for(int i=1;i<mod;i++)
{
fac[i]=fac[i-1]*i%mod;
facinv[i]=facinv[i-1]*inv[i]%mod;
}
}
long long comb(int n,int m)
{
if(m<0||m>n) return 0;
return fac[n]*facinv[m]%mod*facinv[n-m]%mod;
}
long long exgcd(long long a,long long b,long long &x,long long &y)
{
if(b==0)
{
x=1,y=0;
return a;
}
long long x1,y1;
int gcd=exgcd(b,a%b,x1,y1);
x=y1;
y=x1-(a/b)*y1;
return gcd;
}
long long lucas(long long n,long long m)
{
long long res=1;
while(n&&m)
{
res=res*comb(n%mod,m%mod)%mod;
n/=mod,m/=mod;
}
return res;
}
int main()
{
long long m,a,b;
cin>>m>>a>>b>>mod;
init();
m-=1;
long long x,y;
int g=exgcd(a,b,x,y);
if(m%g)
{
cout<<"no"<<endl;
return 0;
}
long long ans=1e18;
long long ax=0;
long long a0=a/g;
long long b0=b/g;
long long x1=x*(m/g);
x1%=b0;
if(x1<0) x1+=b0;
long long y1=(m-a*x1)/b;
if(y1<=0)
{
ans=x1-y1;
ax=x1;
}
long long y2=y*(m/g);
y2%=a0;
if(y2>0) y2-=a0;
long long x2=(m-b*y2)/a;
if(ans>x2-y2)
{
ans=x2-y2;
ax=x2;
}
cout<<lucas(ans,ax)<<endl;
return 0;
}