【数论】求解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为任意整数)

3,可以用exgcd 求 ax+by=gcd(a,b) 得到x0 y0,所以ax+by=c 的解x0=x0/gcd(a,b)*c  y0=y0/gcd(a,b)*c;

 

所以做法是先判断有没有解,然后有的话 就除去它们的公因数,然后由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;
    }
}

inline ll exgcd(ll a,ll b,ll &x,ll &y)
{
    if(!b){
        x=1;y=0;
        return a;
    }
    ll g=exgcd(b,a%b,x,y);
    ll t=x;
    x=y;
    y=t-a/b*y;
    return g;
}

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;

    ll x0,y0,x,y;
    exgcd(a,b,x0,y0);
    x0*=n;y0*=n;
    ll up=y0/a,down=-x0/b;
    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;
        ans=(ans+Lucas(x+y,min(x,y)))%mod;
    }
    printf("%lld\n",ans);
    return 0;
}

 

posted @ 2019-12-02 10:06  草丛怪  阅读(865)  评论(0编辑  收藏  举报