题目描述
有一个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);
}

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

被折叠的 条评论
为什么被折叠?



