// Expressions (括號運算式)// PC/UVa IDs: 110604/10157, Popularity: C, Success rate: average Level: 2// Verdict: Accepted// Submission Date: 2011-06-03// UVa Run Time: 0.668s//// 著作權(C)2011,邱秋。metaphysis # yeah dot net//// 如何得到相應的遞推關係?翻看了 Catalan 數的推導過程得到了一些啟發。首先,括弧的數目必須是// 偶數才可能得到合法的運算式,當 n 為奇數時,不能得到任何深度的正確運算式,同時若深度超過 n// 個括弧所能得到的合法運算式的最大深度時,結果也將是 0 。所有深度為 1 的表示式都只有 1 種,假// 設 T(m,d) 表示括弧對數為 m (n = 2 * m),深度不超過 d 的合法括號運算式的總數,則有://// T(0,d) = 1//// 對於其他情況,假設 E 是一個深度為 d ,括弧對數為 m 的合法運算式,則運算式 E 的最左邊的括弧// l 一定和某個右括弧 r 配對,他們合在一起把運算式劃分為兩個合法的括號運算式,在 l 和 r 之間的// 部分以及 r 右邊的部分,即://// E = (X)Y//// 假設左邊部分有 k 對括弧,則右邊部分有 n - k - 1 對括弧,因為 l 和 r 已經用了一對括弧,則括弧// 運算式 X 的深度最大為 d - 1 ,括號運算式 Y 的深度最大為 d 。則括弧對數為 m,深度為 d 的合法// 運算式數量為 T(m,d) - T(m,d - 1)。//// T(m,d) = T(0,d - 1) * T(m - 1,d) + T(1,d - 1) * T(m - 2,d) + …… + T(k,// d - 1) * T(m - k - 1,d),0 <= k <= m - 1。//// 看起來像是 Catalan 數的二維版本。//// 用於 How Many Pieces of Land? 問題的大數類在這個問題上,顯然效率不夠高,於是我改進了一下,以// 10000為基數,數位改用模10000的值,用不帶正負號的整數來儲存數位。#include <iostream>#include <vector>#include <algorithm>#include <iomanip>using namespace std;#define MAXM151#define MAXD151class integer{friend ostream & operator<<(ostream &, const integer &);public:integer() { };integer(unsigned int a){while (a){digits.push_back(a % base);a /= base;}};~integer() { };integer operator+(integer);integer operator-(integer);integer operator*(integer);private:void zero_justify(void);// 表示大數的結構。數位按模 10000 從低位到高位每 4 位為一組,作為一個 int 型數存放。vector < unsigned int > digits;static unsigned int const base = 10000;static unsigned int const length = 4;};// 重載輸出符號 <<。ostream& operator<<(ostream& os, const integer& n){os << n.digits[n.digits.size() - 1];for (int i = n.digits.size() - 2; i >= 0; i--)os << setw(n.length) << setfill('0') << n.digits[i];return os;}// 移除大數運算產生的前置 0。void integer::zero_justify(void){for (int i = digits.size() - 1; i >= 1; i--){if (digits[i] == 0)digits.erase(digits.begin() + i);elsebreak;}}// 加法運算。integer integer::operator+(integer b){integer c;c.digits.resize(max(digits.size(), b.digits.size()) + 1);int carry = 0;// 進位。int marker = 0;// 為兩數添加前置 0,以使得數位相同,便於計算。while (digits.size() < c.digits.size())digits.push_back(0);while (b.digits.size() < c.digits.size())b.digits.push_back(0);// 逐位相加。while (marker < c.digits.size()){int t = digits[marker] + b.digits[marker] + carry;carry = t / base;c.digits[marker] = t % base;marker++;}c.zero_justify();return c;}// 減法。integer integer::operator-(integer b){integer c;int borrow = 0;// 借位。int marker = 0;// 計數器。// 為減數添加前置0,便於計算。while (b.digits.size() < digits.size())b.digits.push_back(0);// 從低位開始逐位相減,不夠的向高位借位。while (marker < digits.size()){int v = digits[marker] - borrow - b.digits[marker];if (v < 0){v += base;borrow = 1;}elseborrow = 0;c.digits.push_back(v % base);marker++;}c.zero_justify();return c;}// 乘法。integer integer::operator*(integer b){integer c;c.digits.resize(digits.size() + b.digits.size());fill(c.digits.begin(), c.digits.end(), 0);for (int i = 0; i < b.digits.size(); i++)for (int j = 0; j < digits.size(); j++){c.digits[i + j] += digits[j] * b.digits[i];c.digits[i + j + 1] += c.digits[i + j] / base;c.digits[i + j] %= base;}c.zero_justify();return c;}integer result[MAXM][MAXD];void init(){for (int m = 0; m < MAXM; m++)for (int d = 0; d < MAXD; d++)result[m][d] = integer(0);for (int d = 0; d < MAXD; d++)result[0][d] = integer(1);for (int m = 1; m < MAXM; m++)for (int d = 1; d < MAXD; d++)for (int k = 0; k <= m - 1; k++)result[m][d] =result[m][d] + result[k][d -1] * result[m - k - 1][d];}int main(int ac, char *av[]){int n, d;init();while (cin >> n >> d)cout << (result[n / 2][d] - result[n / 2][d - 1]) << endl;return 0;}