【数论题】求解ax+by=c x,y为非负数的解的个数 以及 lucas定理

Comet OJ - 模拟赛 #2 Day1 小猪佩奇跳格子

求解ax+by=n x,y为非负数的解的个数 ,答案对10007取模

(然后这道题x个a和y个b的顺序不同,算作不同)

然后数据范围:a,b,n<=10^9 。

当场翻了数论书,有这几个点:

1,对于二元一次不定方程ax+by=c,有整数解的充要条件是 (a,b)|c。

2,若(a,b)=1,且不定方程有整数解x=x0,y=y0,则它的一切整数解可表示成:

  x=x0+bt; y=y0-at;  (t为任意整数)

所以做法是先判断有没有解,然后有的话 就除去它们的公因数,然后由xy的非负性判断t的范围

然后因为这道题x个a和y个b的顺序不同,算作不同,然后就是组合数了,然后因为是小模,所以是lucas定理。。。

(我居然现场翻书学的lucas,大一学的然后忘了。。可怕QAQ

#include<bits/stdc++.h>
#define debug1 printf("!");
#define debug2 puts("#");

using namespace std;
typedef long long ll;
const int mod=1e4+7;
const int maxn=1e7+50;
const int inf=0x3f3f3f3f;

ll jc[mod+50];
ll kpow(ll a,ll b)
{
    a%=mod;
    ll ans=1;
    while(b)
    {
        if(b&1)ans=ans*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return ans;
}
ll C(ll n,ll m){
    if(m>n)return 0;
    return ((jc[n]*kpow(jc[m],mod-2))%mod*kpow(jc[n-m],mod-2)%mod);
}
ll Lucas(ll n,ll m){
    if(!m)return 1;
    return C(n%mod,m%mod)*Lucas(n/mod,m/mod)%mod;
}
inline void init()
{
    jc[0]=1;
    for(int i=1;i<=mod;i++)
    {
        jc[i]=(jc[i-1]*i)%mod;
    }
}


int f[maxn];

inline void find(ll a,ll b,ll c,ll&x,ll&y)
{
    //t=c/a;tt=c%a;
    //x=t+(tt-b*y)/a   a|(tt-b*y)
    ll t=c/a,tt=c%a;
    y=0;
    while((tt-b*y)%a)y++;
    x=t+(tt-b*y)/a;
    return;
}
int main()
{
    init();
    ll n,a,b,d;
    scanf("%lld%lld%lld",&n,&a,&b);
    if(a>b)swap(a,b);
    d=__gcd(a,b);
    if(n%d!=0)
    {
        puts("0");return 0;
    }
    n/=d;a/=d;b/=d;
    if(n<maxn)
    {
        f[0]=1;
        for(int i=1;i<=n;i++)
        {
            if(a<=i)f[i]=(f[i]+f[i-a])%mod;
            if(b<=i)f[i]=(f[i]+f[i-b])%mod;
//            printf("<%d %d>",f[i],i);
        }
        printf("%d",f[n]);
        return 0;
    }

    ll x0,y0,x,y;
    find(a,b,n,x0,y0);
//    printf("{x0=%lld,y0=%lld}\n",x0,y0);
    ll up=y0/a,down=-x0/b;
//    printf("{down=%lld,up=%lld}",down,up);
    ll ans=0;
    for(int t=down;t<=up;t++)
    {
        x=x0+b*t;y=y0-a*t;
        if(t<0&&x<0)break;
        if(t>0&&y<0)break;
        if(x<0||y<0||x>n||y>n)continue;
//        printf("<%lld %lld>",x,y);
        ans=(ans+Lucas(x+y,min(x,y)))%mod;
    }
    printf("%lld\n",ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/kkkek/p/11969080.html