【NOIP模拟】德拉曼兹路基

来源:互联网 发布:新还珠格格3知画生产 编辑:程序博客网 时间:2024/06/10 01:50

Description

阿良良木历将要迎来人生(不,是吸血鬼生涯)的第一次战斗——与同为吸血鬼的德拉曼兹路基在直江津高中的操场solo,以取回Heartunderblade的右脚。
德拉曼兹路基是个2米高的彪形大汉,拥有吸血鬼的能力,双手拿着焰形巨剑(那巨剑似乎是flamberge的一种),所以历发现直接对抗是不利的。直江津高中是历熟悉的地方,也就是所谓“地利”,历看到了体育仓库,并打算前往那里寻找武器。
可以把操场看成一个网格图,历开始时位于(0,0),仓库位于(n,m)。对于每一步,假设历位于(x,y),在德拉曼兹路基的攻击下,历只能向下一格(x+1,y),或向右一格(x,y+1)移动,当然他还可以借助吸血鬼的体能,直接跳到(p,q)(p,q自己选定且需满足p>x,q>y)。
如果历普通地向下、向右移动超过k步,那么他会被德拉曼兹路基追上,并且被打断一只手。吸血鬼的再生能力强,四肢是马上还原的,但是历不想受这个疼痛,于是他想知道,在不被追上的情况下,有多少种不同的方案可以到达仓库。历要求不高,只要知道答案对1000000007取模的答案即可。

Solution

比赛的时候,脑子短路没推出来。比赛后推完式子,去问出题人一个问题,由于出题人没有明白问题就否定了我,导致我很久之后才改这道题。
首先可以向右或向下走k步,那么就枚举向下或向右的步数,然后再用组合数去算跳的步数。
比如说枚举向下向右后走到了(i,j)点,然后现在需要跳。因为行的情况和列的情况互不影响,所以行和列单独算然后再乘起来就好了。就相当于在(i,j)的右下角的矩阵中跳,那么枚举跳l次,那么跳的情况数就是Cl1ni1Cl1mi1因为最后一个肯定要到最后一行最后一列,那么i+1~n-1(j+1~n-1)随意。然后因为向下向右和跳可以交叉的用,那么再乘上CanCbna=n!a!b!l!=(a+b+l)!a!b!l!(n表示向右向下跳的总步数,a表示向下步数,b表示向右步数,l表示跳的步数)。

Code

#include<iostream> #include<cstdio>#include<cstring>#include<cmath>#include<algorithm>#define fo(i,a,b) for(i=a;i<=b;i++)#define fod(i,a,b) for(i=a;i>=b;i--)using namespace std;typedef long long ll;const int maxn=200007,mo=1000000007;ll i,j,k,l,t,n,m,ans,a,b,nn,mm;ll fact[maxn],ni[maxn];ll c(ll x,ll y){    return fact[x]*ni[y]%mo*ni[x-y]%mo;    }ll qsm(ll x,ll y){    ll z=1;    while(y){        if(y&1)z=z*x%mo;        x=x*x%mo;        y/=2;    }    return z;}int main(){    scanf("%lld%lld%lld",&n,&m,&k);    if(n<m)swap(n,m);    fact[0]=1;    fo(i,1,n*2)fact[i]=fact[i-1]*i%mo;    ni[0]=ni[1]=1;    ni[n*2]=qsm(fact[n*2],mo-2);    fod(i,n*2-1,1)ni[i]=ni[i+1]*(i+1)%mo;    fo(a,0,k){        fo(b,0,k-a){            nn=n-a;mm=m-b;            l=min(nn,mm);            if(!nn&&!mm){ans=(ans+c(a+b,a))%mo;continue;}            fo(i,1,l){               (ans+=c(nn-1,i-1)*c(mm-1,i-1)%mo*fact[a+b+i]%mo*ni[a]%mo*ni[b]%mo*ni[i]%mo)%=mo;            }        }    }    printf("%lld\n",ans);}
1 0
原创粉丝点击