# ARC183-C Not Argmax 2018diff https://atcoder.jp/contests/arc183/tasks/arc183_c ## 解法 適切に前計算を行っておくことで、「$N$をおいてはいけない場所」がわかる。$N$を置いて良い場所を一つ固定して($i$)、そこに$N$を置いたとする。そうすると$L_{j}\le i\le R_{j}$なる区間の条件はすべて無視して良いことになる。なぜならそのような区間の$\text{argmax}$は$\{ i\}$だから。 結局の所$i$を固定すると$(P_{1}, P_{2}, \dots, P_{i - 1})$と$(P_{i + 1}, P_{i + 2}, \dots, P_{N})$それぞれについて同様の問題を解くことに帰着する。これは区間DPをやれば良いという兆し。 DPの計算と入力を受け取るのがボトルネックで、 $\Theta (N^{3} + M)$ ## 実装 ```cpp= #include <bits/stdc++.h> const long long MOD{998244353}; const int MAX{510}; long long comb[MAX][MAX]; long long add(long long a, long long b) { a += b; if (a >= MOD) a -= MOD; return a; } long long mul(long long a, long long b) { return a * b % MOD; } int main() { std::cin.tie(nullptr)->sync_with_stdio(false); for (int i{} ; i < MAX ; i++) comb[i][0] = comb[i][i] = 1; for (int i{1} ; i < MAX ; i++) for (int j{1} ; j < i ; j++) comb[i][j] = add(comb[i - 1][j - 1], comb[i - 1][j]); int N, M; std::cin >> N >> M; std::vector ng(N, std::vector(N + 1, std::vector<bool>(N))); for (int i{} ; i < M ; i++) { int L, R, X; std::cin >> L >> R >> X; L--; X--; ng[L][R][X] = true; } for (int len{1} ; len <= N ; len++) { for (int l{} ; l + len <= N ; l++) { int r{l + len}; if (l) { for (int i{l} ; i < r ; i++) ng[l - 1][r][i] = ng[l - 1][r][i] | ng[l][r][i]; } if (r < N) { for (int i{l} ; i < r ; i++) ng[l][r + 1][i] = ng[l][r + 1][i] | ng[l][r][i]; } } } std::vector dp(N + 1, std::vector<long long>(N + 1, -1LL)); auto rec{[&](auto rec, int l, int r) -> long long { if (dp[l][r] != -1) return dp[l][r]; long long& res{dp[l][r]}; res = 0; if (l == r) return res = 1; for (int m{l} ; m < r ; m++) if (!ng[l][r][m]) { res = add(res, mul(rec(rec, l, m), mul(rec(rec, m + 1, r), comb[r - l - 1][m - l]))); } return res; }}; std::cout << rec(rec, 0, N) << '\n'; } ``` [提出](https://atcoder.jp/contests/arc183/submissions/58390369) ## 感想 この回はコンテストにオンタイムで参加していて、$120$分ほぼ全部これにつっぱして解けなかった思い出がある。感想戦で方針を知っていた。 当時は包除原理ばっかり考えていて詰んでいた。変に解法をエスパーするよりも、堅実に観察・定式化を積み上げる考察を心がけたいものだ。