1. 程式人生 > >hihocoder第238周:楊氏矩陣的個數

hihocoder第238周:楊氏矩陣的個數

條件 tdi 公式 ++ http 鏈接 n! 元素 擴展歐幾裏得算法

題目鏈接

問題描述

給定一個N行M列的矩陣,往裏面填入$1-N\times M$個數字,使得這個矩陣每行、每列都滿足遞增。問:有多少種填法?

問題分析

這個問題很難,如果能夠直接想到,那就是天才了。
此問題中描述的矩陣就是楊氏矩陣的特例。楊氏矩陣又叫楊氏圖表。
楊氏圖表,它是這樣一個二維表,滿足條件:
(1)如果格子(i,j)沒有元素,則它右邊和上邊的相鄰格子也一定沒有元素。
(2)如果格子(i,j)有元素a[i,j],則它右邊和上邊的相鄰格子要麽沒有元素,要麽有元素且比a[i][j]大。

楊氏矩陣的計數公式為:
$$count=\frac{n!}{\sum_{x \in Grids}{hook(x)}}

$$

其中$hook(x)$表示格子x下方、右方的空白格點數(不包括它自己)之和+1。

關鍵方法

由楊氏矩陣的計數公式可知,此問題是一道數學題。關鍵在於模除運算,這可以通過擴展歐幾裏得算法求逆元來實現。

#include<stdio.h>
#include<iostream>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
ll gcd(ll a, ll b, ll &x, ll &y) {
    if (b == 0) {
        x = 1, y = 0;
        return a;
    }
    ll q = gcd(b, a%b, y, x);
    y -= a / b * x;
    return q;
}
ll reverse(int v) {
    ll x, y;
    ll g = gcd(v, mod, x, y);
    return x;
}
int main() {
    int n, m;
    cin >> n >> m;
    ll s = 1;
    for (int i = 1; i <= n * m; i++) {
        s *= i;
        s %= mod;
    }
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            int hook = n - i + m - j-1;
            int r = reverse(hook);
            s *= r;
            s %= mod;
        }
    }
    s = (s + mod) % mod;
    cout << s << endl;
    return 0;
}

hihocoder第238周:楊氏矩陣的個數