素朴な DP でもできるけど、実はカタラン数!
問題概要
正の整数 が与えられる。以下の条件を満たす数列 (数列のサイズ は 以上の範囲で自由に選んでよい) の個数を求めよ。
- 各 に対して、 である
- である
たとえば、 のとき、 の 4 通りがある。
制約
考えたこと
素朴に DP でもできるが、実はカタラン数!!
たとえば のとき、 は条件を満たすが、これは下図の左のように図示できる。そしてそれは、下図の右のように グリッドの最短経路に対応する。
という条件は、グリッドの対角線の下側のみを使うということに対応する。これはカタラン数そのものである。
よって、答えは「 番目のカタラン数 - 1」である (1 を引くのは右下を通る最短経路を除外するため)。つまり、
を答えればよい。計算量は となる。
コード
オーバーフロー対策のために、__int128_t
型を用いた。
#include <bits/stdc++.h> using namespace std; // オーバーフロー対策 using u128 = __int128_t; class FIELDDiagrams { public: long long countDiagrams(int fieldOrder) { long long N = fieldOrder + 1; // N 番目のカタラン数 - 1 // C(2N, N) / (N+1) u128 res = 1; for (long long i = 1; i <= N; ++i) { res *= N*2 - i + 1; res /= i; } res /= (N+1); // N 番目のカタラン数 return (res - 1); } }; // BEGIN CUT HERE namespace moj_harness { int run_test_case(int); void run_test(int casenum = -1, bool quiet = false) { if (casenum != -1) { if (run_test_case(casenum) == -1 && !quiet) { cerr << "Illegal input! Test case " << casenum << " does not exist." << endl; } return; } int correct = 0, total = 0; for (int i=0;; ++i) { int x = run_test_case(i); if (x == -1) { if (i >= 100) break; continue; } correct += x; ++total; } if (total == 0) { cerr << "No test cases run." << endl; } else if (correct < total) { cerr << "Some cases FAILED (passed " << correct << " of " << total << ")." << endl; } else { cerr << "All " << total << " tests passed!" << endl; } } int verify_case(int casenum, const long long &expected, const long long &received, clock_t elapsed) { cerr << "Example " << casenum << "... "; string verdict; vector<string> info; char buf[100]; if (elapsed > CLOCKS_PER_SEC / 200) { sprintf(buf, "time %.2fs", elapsed * (1.0/CLOCKS_PER_SEC)); info.push_back(buf); } if (expected == received) { verdict = "PASSED"; } else { verdict = "FAILED"; } cerr << verdict; if (!info.empty()) { cerr << " ("; for (int i=0; i<(int)info.size(); ++i) { if (i > 0) cerr << ", "; cerr << info[i]; } cerr << ")"; } cerr << endl; if (verdict == "FAILED") { cerr << " Expected: " << expected << endl; cerr << " Received: " << received << endl; } return verdict == "PASSED"; } int run_test_case(int casenum__) { switch (casenum__) { case 0: { int fieldOrder = 2; long long expected__ = 4; clock_t start__ = clock(); long long received__ = FIELDDiagrams().countDiagrams(fieldOrder); return verify_case(casenum__, expected__, received__, clock()-start__); } case 1: { int fieldOrder = 3; long long expected__ = 13; clock_t start__ = clock(); long long received__ = FIELDDiagrams().countDiagrams(fieldOrder); return verify_case(casenum__, expected__, received__, clock()-start__); } case 2: { int fieldOrder = 5; long long expected__ = 131; clock_t start__ = clock(); long long received__ = FIELDDiagrams().countDiagrams(fieldOrder); return verify_case(casenum__, expected__, received__, clock()-start__); } // custom cases case 3: { int fieldOrder = 29; long long expected__ = 3814986502092303LL; clock_t start__ = clock(); long long received__ = FIELDDiagrams().countDiagrams(fieldOrder); return verify_case(casenum__, expected__, received__, clock()-start__); } case 4: { int fieldOrder = 30; long long expected__ = 14544636039226908LL; clock_t start__ = clock(); long long received__ = FIELDDiagrams().countDiagrams(fieldOrder); return verify_case(casenum__, expected__, received__, clock()-start__); } /* case 5: { int fieldOrder = ; long long expected__ = ; clock_t start__ = clock(); long long received__ = FIELDDiagrams().countDiagrams(fieldOrder); return verify_case(casenum__, expected__, received__, clock()-start__); }*/ default: return -1; } } } int main(int argc, char *argv[]) { if (argc == 1) { moj_harness::run_test(); } else { for (int i=1; i<argc; ++i) moj_harness::run_test(atoi(argv[i])); } } // END CUT HERE