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 ",ans); return 0; }