【51Nod 1362】搬箱子

45 篇文章 0 订阅

Description

有一个n*m的棋盘,左上角为(0,0),右下角为(n,m).在左上角有一个箱子(箱子是放在交叉点上的)。现在要把箱子搬到最后一排。搬的时候只有向右,向下,或者向右下方走一步。也就是说,假如箱子在(x,y),那么下一步只能把他搬到(x+1,y)或(x,y+1)或(x+1,y+1).

问有多少种的走法可以把箱子从左上角搬到最后一排。由于数目巨大,对X取余输出即可。

Solution

设f[n][p]为走到(n,p)的方案数,那么答案= mq=1f[n][p]
那么我们现在要想一想怎么求f[n][p]
因为斜向下走的方案数不固定,所以肯定要枚举。
先假设n≤m
设斜着走i个
那么 f[n][m]=ni=0Cin+miCnin+m2i
=ni=0CinCmin+mi
考虑怎么化简f[n][m]。
ans=ni=0Cinmq=1Cqin+qi
我们知道 Cba+b+1=bi=0Cia+i
所以 ans=ni=0CinCmin+mi+1
然后求一下组合数。
但是mod数不是质数,所以不能用lucas
有两种方法:
1、暴力分解质因数,然后把质因数抵消之后直接求
2、考虑到有一些质因数是mod数没有的,那么肯定和mod数互质,所以有逆元,那么就不是所有的质因数都要分解掉。

Code

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const int maxn=40007;
int i,j,k,l,t,n,m,mo,x;
int a[140],p[maxn],shu[maxn],z,pi[140];
ll ans;
bool bz[maxn];
inline ll qsm(ll x,ll y){
    ll z=1;
    for(;y;y/=2,x=x*x%mo)if(y&1)z=z*x%mo;
    return z;
}
inline void suan(ll x,ll y){
    ll i,q=x;
    fo(i,1,p[0]){
        if(pi[i]>q)break;
        while(q%p[i]==0)a[i]+=y,q/=p[i];
    }
    if(q>1){
        if(q<800)a[shu[q]]+=y;else z=z*q%mo;
    }
}
inline void c(ll x,ll y){
    if(x<=y)return;
    ll i;
    fo(i,x-y+1,x)suan(i,1);
    fo(i,1,y)suan(i,-1);
   /* fo(i,1,p[0]){
        if(p[i]>x)break;
        if(a[i])z=z*qsm(p[i],a[i])%mo;
    }*/
   // return z;
}
int main(){
    fo(i,2,800){
        if(!bz[i])p[++p[0]]=i,shu[i]=p[0],pi[p[0]]=i*i;
        fo(j,1,p[0]){
            t=p[j]*i;if(t>800)break;
            bz[t]=1;if(i%p[j]==0)break;
        }
    }
//  freopen("fan.in","r",stdin);
    scanf("%d%d%d",&n,&m,&mo);
    fo(i,0,n){
        if(i>m)break;
        z=1;memset(a,0,sizeof(a));
        c(n-i+m+1,n+1),c(n,i);
        fo(j,1,p[0]){
            if(a[j])z=z*qsm(p[j],a[j])%mo;
        }
        ans=ans+z;
        if(ans>=mo)ans-=mo;
    }
    printf("%lld\n",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值