#include #include #define fast std::ios::sync_with_stdio(false); std::cin.tie(nullptr); std::cout.tie(nullptr) #define eb emplace_back #define all(x) (x).begin(), (x).end() using namespace std; using ll = long long; using mint = atcoder::modint998244353; constexpr ll inf = 2e18; constexpr ll mod = 998244353; static void judge(bool c) { std::cout << (c ? "Yes" : "No") << '\n'; } // https://nyaannyaan.github.io/library/tree/cartesian-tree.hpp.html template pair>, int> CartesianTree(vector &a) { int N = (int)a.size(); vector> g(N); vector p(N, -1), st; st.reserve(N); for (int i = 0; i < N; i++) { int prv = -1; while (!st.empty() && a[i] < a[st.back()]) { prv = st.back(); st.pop_back(); } if (prv != -1) p[prv] = i; if (!st.empty()) p[i] = st.back(); st.push_back(i); } int root = -1; for (int i = 0; i < N; i++) { if (p[i] != -1) g[p[i]].push_back(i); else root = i; } return make_pair(g, root); } ll n,a[200000]; int main(){ cin >> n; vector p(n); set zero,still; for(int i = 0; i < n; i++){ cin >> a[i]; if(!a[i]) zero.insert(i); still.insert(i); } // 操作列を1つ構成する for(int i = 0; i < n; i++){ if(zero.empty()){ cout << 0 << endl; return 0; } int v = *zero.begin(); zero.erase(zero.begin()); p[v] = n - i; a[v] = inf; still.erase(v); auto it1 = still.lower_bound(v),it2 = still.upper_bound(v); if(it1 != still.begin()){ it1--; a[*it1]--; if(a[*it1] == 0){ zero.insert(*it1); } if(a[*it1] < 0){ cout << 0 << endl; return 0; } } if(it2 != still.end()){ a[*it2]--; if(a[*it2] == 0){ zero.insert(*it2); } if(a[*it2] < 0){ cout << 0 << endl; return 0; } } } // カルテシアンツリーを作る auto [g, par]= CartesianTree(p); // すべての頂点について部分木のサイズを求める vector sz(n, 1); auto dfs = [&](auto&& f, int v) -> void { for(auto nv : g[v]){ f(f, nv); sz[v] += sz[nv]; } }; dfs(dfs, par); // 答えの計算 mint ans = 1; for(int i = 0; i < n; i++){ ans /= sz[i]; ans *= (i + 1); } cout << ans.val() << endl; }