拓展歐幾里得經典題(+盧卡斯定理)

題意:現在你在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;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章