[51nod 1362]搬箱子

本文探讨了一个n*m棋盘上箱子从左上角到最后一排的移动路径计数问题,仅允许向右、向下或对角线下移。通过枚举斜走步数并运用组合数公式得出解法,最终通过代码实现。

题目描述

有一个n*m的棋盘,左上角为(0,0),右下角为(n,m).在左上角有一个箱子(箱子是放在交叉点上的)。现在要把箱子搬到最后一排。搬的时候只有向右,向下,或者向右下方走一步。也就是说,假如箱子在(x,y),那么下一步只能把他搬到(x+1,y)或(x,y+1)或(x+1,y+1).
问有多少种的走法可以把箱子从左上角搬到最后一排。由于数目巨大,对X取余输出即可。

题目大意

考虑走到(n,j)的方案数?
枚举i表示斜走的步数,可以推出个组合数公式。
要对不同j求和。
套入下面这个式子就很简单了:
mi=0Cin=Cm+1n+1
组合数因为最后拆出来项数不多,也是可以算的
详见代码

#include<cstdio>
#include<algorithm>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
typedef long long ll;
const int maxn=800+10;
int p[30];
int num[maxn*2][30],sum[maxn][30],f[maxn],g[maxn*2];
int i,j,k,l,t,n,m,mo,top,ans,xx,yy;
int quicksortmi(int x,int y){
    if (!y) return 1;
    int t=quicksortmi(x,y/2);
    t=(ll)t*t%mo;
    if (y%2) t=(ll)t*x%mo;
    return t;
}
void gcd(int a,int b){
    if (!b){
        xx=1;yy=0;
        return;
    }
    gcd(b,a%b);
    int x=yy,y=xx-a/b*yy;
    xx=x;yy=y;
}
int getny(int x){
    gcd(x,mo);
    return (xx%mo+mo)%mo;
}
int main(){
    scanf("%d%d%d",&n,&m,&mo);
    //if (n>m) swap(n,m);
    k=mo;
    t=floor(sqrt(mo));
    fo(i,2,t)
        if (k%i==0){
            p[++top]=i;
            while (k%i==0) k/=i;
        }
    if (k>1) p[++top]=k;
    fo(j,1,top){
        t=p[j];
        fo(i,1,n+1){
            k=i;
            while (k%t==0){
                sum[i][j]++;
                k/=t;
            }
            sum[i][j]+=sum[i-1][j];
        }
    }
    f[0]=1;
    fo(i,1,n+1){
        k=i;
        fo(j,1,top){
            t=p[j];
            while (k%t==0) k/=t;
        }
        f[i]=(ll)f[i-1]*k%mo;
    }
    fo(j,1,top){
        t=p[j];
        fd(i,n+m+1,max(m-n+1,1)){
            k=i;
            while (k%t==0){
                num[n+m-i+2][j]++;
                k/=t;
            }
            num[n+m-i+2][j]+=num[n+m-i+1][j];
        }
    }
    g[0]=1;
    fd(i,n+m+1,max(m-n+1,1)){
        k=i;
        fo(j,1,top){
            t=p[j];
            while (k%t==0) k/=t;
        }
        g[n+m-i+2]=(ll)g[n+m-i+1]*k%mo;
    }
    fo(i,0,min(n,m)){
        t=(ll)g[n+m-(m-i+1)+2]*getny(g[n+m-(n+m-i)])%mo*f[n]%mo;
        t=(ll)t*getny(f[i])%mo*getny(f[n-i])%mo*getny(f[n+1])%mo;
        fo(j,1,top){
            k=num[n+m-(m-i+1)+2][j]-num[n+m-(n+m-i)][j];
            k+=sum[n][j];
            k-=sum[i][j];
            k-=sum[n-i][j];
            k-=sum[n+1][j];
            t=(ll)t*quicksortmi(p[j],k)%mo;
        }
        (ans+=t)%=mo;
    }
    //fo(i,1,n) ans=(ll)ans*i%mo;
    printf("%d\n",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值