1. 程式人生 > >hdu 5698 組合數

hdu 5698 組合數

瞬間移動

Problem Description

有一個無限大的矩形,初始時你在左上角(即第一行第一列),每次你都可以選擇一個右下方格子,並瞬移過去(如從下圖中的紅色格子能直接瞬移到藍色格子),求到第nn行第mm列的格子有幾種方案,答案對10000000071000000007取模。

http://acm.hdu.edu.cn/data/images/C702-1003-1.jpg

Input

多組測試資料。

兩個整數n,m(2\leq n,m\leq 100000)n,m(2n,m100000)

Output

一個整數表示答案

Sample Input
4 5
Sample Output
10

Source

分析:

畫幾個數之後就可以發現規律了,可以看出是一個傾斜45度的楊輝三角,每個位置的的數相當於:從楊輝三角第(n+m-4)層,取m-2個數。

因為會有除法取模,所以要用逆元。

有兩個公式:

這兩個公式實際上是等價的,只是用程式碼實現的時候會有所差別。

我是用第二個寫的:

#include<cstdio>
#include<cstring>
const long long mod=1000000007;
typedef long long ll;
const int N=100002;
ll inv[N];
int main()
{
    int n,m;
    inv[1]=1;
    for(int i=2;i<N;i++)inv[i]=(mod-mod/i)*inv[mod%i]%mod;
    while(~scanf("%d%d",&n,&m)){
        if(n==1||m==1){
            printf("0\n");continue;
        }
        n=n+m-4;
        m=m-2;
        ll ans=1;
        for(int i=1;i<=m;i++){
            ans=(ans*(ll)(n-i+1))%mod;
            ans=(ans*inv[i])%mod;
        }
        printf("%lld\n",ans);
    }
    return 0;
}
當然逆元也有很多種實現方法:

可以用擴充套件歐幾里德定理,費馬小定理(這需要快速冪實現),或者上面那個遞推公式。

對於這題,是用楊輝三角來做的,也有人的思路是:

給我們一個座標,我們可以得出可以移動到的區域即(n-2)*(m-2)。列舉要在這個區域停留幾次,C(y,k),表示從y列中選k列去停,C(x,k)表示從x行中選哪k行去停。所以乘積累加即為結果。

這種《問題分解》的思想是值得學習的,就像UVa 11134這題的思想是一致的。