http://www.oj.swust.edu.cn/problem/show/2493
Description
ACM集训队暑期集训正在火热集训,但是实验室“男神”小红遇到了难题,我们从底层到达实验室需要走N阶楼梯,小红每次只能走x阶或y阶,并且他还需要到达第A阶和第B阶楼梯,请你帮他计算他到达第N阶楼梯有多少种方法。
Input
多组输入 N,x,y,A,B 0 < N < 10000, 0 < x,y < 10000(x!=y), 0< A , B < N
Output
方法数mod 1000000007
3 1 2 2 1 5 2 3 1 1
思路:A=MIN(A,B),B=MAX(A,B); 一个人从原点到A方案数*A-B方案数*N-B方案数
以源点-->A为例 : ax+by=A; 求所有a,b,的解,很显然是扩展欧几里得求不定方程。
如果A%gcd(a,b)!=0,无解,即方案数为0;
并且人只能以两种方式跳,假如跳了a次x步,b次y步,那么方案数为 (a+b)!/(a!+b!)%mod,再求一次逆元就ok了。
#include<iostream>
#include<string>
#include<stdio.h>
#include<string.h>
#include<vector>
#include<math.h>
#include<queue>
#include<map>
#include<set>
#include<algorithm>
using namespace std;
#define maxn 1005
#define INF 0x3f7f7f7f
const double eps = 1e-10;
#define LL long long
const LL mod = 1000000007;
LL ans1,ans2,ans3;
LL c[1000000];
void init()//初始化 num!
{
c[0]=1;
for(int i=1;i<1000000;i++)
{
c[i]=i*c[i-1];
c[i]%=mod;
}
}
void exgcd(LL a,LL b,LL &d,LL &x,LL& y)
{
if(b==0)
{
d=a;x=1;y=0;
return ;
}
exgcd(b,a%b,d,x,y);
LL t=x;
x=y;
y=t-a/b*y;
}
LL get(LL a,LL x)//二分幂
{
LL ans=1;
while(x)
{
if(x%2)ans=(ans*a)%mod;
x/=2;
a=(a*a)%mod;
}
return ans;
}
LL fun(LL A,LL x,LL y,LL x0,LL y0,LL d,LL n)
{
LL ans=0;
LL tt=A/d;
LL x1,y1;
x1=(LL)(x0*(LL)tt);
y1=(LL)(y0*(LL)tt);
for(LL i=-n;i<=n;i++)
{
LL xx=x1+(y/d)*i;
LL yy=y1-(x/d)*i;
if(xx<0||xx>n||yy<0||yy>n)continue;
if(xx*x+yy*y==A)
{
LL temp=c[xx+yy];
LL temp1=c[xx];
temp1=(temp1*c[yy])%mod;
ans+= (temp*get(temp1,mod-2))%mod;
ans%=mod;
}
}
return ans;
}
int main()
{
LL n,x,y,A,B,d,x0,y0;
init();
while(scanf("%lld %lld %lld %lld %lld",&n,&x,&y,&A,&B)!=EOF)
{
if(A>B)swap(A,B);
exgcd(x,y,d,x0,y0);
if(A%d!=0)
{
puts("0");
continue;
}
ans1=fun(A,x,y,x0,y0,d,n);
//cout<<ans1<<"****"<<endl;
if(B!=A)
{
if((B-A)%d!=0)
{
puts("0");
continue;
}
ans2=fun(B-A,x,y,x0,y0,d,n);
if((n-B)%d!=0)
{
puts("0");
continue;
}
ans3=fun(n-B,x,y,x0,y0,d,n);
}
else
{
ans2=1;
if((n-B)%d!=0)
{
puts("0");
continue;
}
ans3=fun(n-B,x,y,x0,y0,d,n);
}
LL ans=(ans1*ans2)%mod;
ans=(ans*ans3)%mod;
printf("%lld\n",ans);
}
return 0;
}