【HDU 5698】 瞬間移動

【題目鏈接】

           點擊打開鏈接

【算法】

          用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\n",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)
*/ 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章