【题目链接】
【算法】
用f[i][j]表示走到(i,j)这个位置有多少种方案,因为走到(i,j)这个位置,上一步一定在它左上角的矩形中,所以,
f(i,j) = sigma( f(x,y) ) ( (x,y)在左上角的矩形中)
我们尝试将它画出来,发现是斜着的杨辉三角
然后,通过找规律,我们发现 : f(n,m) = C(n+m-4,n-2)
求C函数的值,这里有一种方法 :
C(n,r) mod P = (n! / (n - r)! / r!) mod P
= (n!) mod P * inv( (n - r)! ) mod P * inv( r! ) mod P( 其中,inv表示乘法逆元 )
考虑预处理阶乘和阶乘逆元
阶乘很容易求,那么,阶乘逆元怎么求呢?
这里有一种线性求阶乘逆元的方法 ( 如果我们要求 inv( n! ) ) :
inv(n ! ) = inv( (n - 1)! n )
= inv( (n - 1)! ) inv( n )
所以 inv( (n - 1)! ) = inv( n ! ) * inv( inv( n ) )
= inv( n! ) * n
有了这个式子,我们便可以在线性时间内求出所有的阶乘逆元
这一题,我们只要预处理阶乘和阶乘逆元,然后,O(1)回答询问,即可
【代码】
#include<bits/stdc++.h> using namespace std; #define MAXN 200010 const long long P = 1000000007; long long n,m; long long fac[MAXN],inv[MAXN]; inline long long power(long long a,long long n) { long long ans = 1,b = a; while (n > 0) { if (n & 1) ans = (ans * b) % P; b = (b * b) % P; n >>= 1; } return ans; } inline void init() { int i; fac[0] = 1; for (i = 1; i < MAXN; i++) fac[i] = fac[i-1] * i % P; inv[MAXN-1] = power(fac[MAXN-1],P-2); for (i = MAXN - 2; i >= 1; i--) inv[i] = inv[i+1] * (i + 1) % P; } inline long long C(long long n,long long m) { if (!m) return 1; else if (n == m) return 1; else return fac[n] * inv[n-m] % P * inv[m] % P; } int main() { init(); while (scanf("%d%d",&n,&m) != EOF) { printf("%lld ",C(n+m-4,n-2)); } return 0; } /* f( n! ) = f( (n-1)! n) = f( (n - 1)! ) f(n) f( n! ) * f( f(n) ) = f( (n - 1)! ) f( n! ) * n = f( (n - 1)! ) f( n! ) = f ( (n + 1)! ) * (n + 1) */