BZOJ3260 跳 【组合数】
题目
邪教喜欢在各种各样空间内跳。现在,邪教来到了一个二维平面。
在这个平面内,如果邪教当前跳到了(x,y),那么他下一步可以选择跳到以下4个点:
(x-1,y),(x+1,y),(x,y-1),(x,y+1)。
而每当邪教到达一个点,他需要耗费一些体力,
假设到达(x,y)需要耗费的体力用C(x,y)表示。
对于C(x,y),有以下几个性质:
1、若x=0或者y=0,则C(x,y)=1。
2、若x>0且y>0,则C(x,y)=C(x,y-1)+C(x-1,y)。
3、若x<0且y<0,则C(x,y)=无穷大。
现在,邪教想知道从(0,0)出发到(N,M),最少花费多少体力
到达(0,0)点花费的体力也需要被算入)。
由于答案可能很大,只需要输出答案对10^9+7取模的结果。
输入格式
读入两个整数 N ,M,表示邪教想到达的点。
0<=N, M<=10^12 ,N*M<=10^12
输出格式
输出仅一个整数,表示邪教需要花费的最小体力对 10^9+7取模的结果。
输入样例
1 2
输出样例
6
题解
画一下图就发现是一个杨辉三角
先使\(M \le N\)
我们要走到\({N + M \choose M}\)
贪心一下路径就是先走\(N + 1\)个\(1\),再斜着走\(M\)步
尝试改变一下路径就发现这样的贪心没有问题
答案就是
\[N + \sum\limits_{i = 0}^{M} {N + i \choose i}
\]
组合数有一个比较常用的结论就是
\[\sum\limits_{i = 0}^{M} {N + i \choose i} = {N + M + 1 \choose M}
\]
由组合数递推可证明
那么答案就是
\[N + {N + M + 1 \choose M}
\]
由于题目有\(N*M \le 10^{12}\)的限制,所以\(M \le 10^6\),直接算就好了
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define LL long long int
#define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define BUG(s,n) for (int i = 1; i <= (n); i++) cout<<s[i]<<' '; puts("");
using namespace std;
const int maxn = 100005,maxm = 100005,INF = 1000000000,P = 1e9 + 7;
LL N,M;
LL qpow(LL a,LL b){
LL ans = 1;
for (; b; b >>= 1,a = a * a % P)
if (b & 1) ans = ans * a % P;
return ans;
}
int main(){
scanf("%lld%lld",&N,&M);
if (M > N) swap(N,M);
LL ans = 1,ansb = 1;
for (LL i = 1; i <= M; i++){
ans = ans * ((N + M + 2 - i + P) % P) % P;
ansb = ansb * i % P;
}
ans = ((ans * qpow(ansb,P - 2) % P + N) % P + P) % P;
printf("%lld\n",ans);
return 0;
}