您的位置:首页 > 其它

【NOIP模拟】德拉曼兹路基

2016-09-12 14:55 267 查看

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次,那么跳的情况数就是Cl−1n−i−1∗Cl−1m−i−1因为最后一个肯定要到最后一行最后一列,那么i+1~n-1(j+1~n-1)随意。然后因为向下向右和跳可以交叉的用,那么再乘上Can∗Cbn−a=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);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: