2493: 小红的难题 (扩展欧几里得+逆元)

本文介绍了一道典型的ACM竞赛题目——如何计算从起点到达特定楼层的方法数。通过扩展欧几里得算法解决不定方程,并结合组合数学原理计算方案数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ACM集训队暑期集训正在火热集训,但是实验室“男神”小红遇到了难题,我们从底层到达实验室需要走N阶楼梯,小红每次只能走x阶或y阶,并且他还需要到达第A阶和第B阶楼梯,请你帮他计算他到达第N阶楼梯有多少种方法。
多组输入
N,x,y,A,B
0 < N < 10000,  0 < x,y < 10000(x!=y),  0< A , B < N
方法数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;
}

   

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值