#include <iostream>
#include <algorithm>
#include <vector>
#include <utility>
#include <functional>
#include <iomanip>
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <tuple>
#include <stack>
#include <queue>
#include <deque>
#include <bitset>
#include <array>
#include <list>
#include <forward_list>
#include <complex>
#include <numeric>
#include <regex>
#include <boost/optional.hpp>
//#include <boost/multiprecision/cpp_int.hpp>
//#include <boost/range/irange.hpp>

using namespace std;

//#define int Int
#define let auto const&
#define var decltype(auto)
#define ref auto&&

using Int = int_fast64_t;
using Uint = uint_fast64_t;

using ll = long long;
using ld = long double;

template<typename T=int> using vec = vector<T>;
template<typename T=int> using mat = vec<vec<T>>;
template<typename T=int> using dic = map<string, T>;
template<typename T=int> using dict = unordered_map<string, T>;
template<typename T=int> using pqg = priority_queue<T, vector<T>, greater<T>>;
template<typename T=int, class F=less<T>> using pq = priority_queue<T, vector<T>, F>;

using pii = pair<int, int>;
using vi = vector<int>;
using mi = vector<vector<int>>;
using vp = vector<pii>;
using vb = vector<bool>;

template<typename T=int> using opt = boost::optional<T>;
//using cint = boost::multiprecision::cpp_int;
//using boost::irange;

constexpr int IINF = 0x3f3f3f3f;
constexpr long long LINF = 0x3f3f3f3f3f3f3f3fLL;
//template<typename T> constexpr T INF = (T)LINF;
constexpr int INF = (int)LINF;

constexpr long long MOD = 1e9+7;

#define overload4(a,b,c,d,name,...) name
#define rep1(n) for(int i=0;i<(n);++i)
#define rep2(i,n) for(int i=0;i<(n);++i)
#define rep3(i,a,b) for(int i=(a);i<(b);++i)
#define rep4(i,a,b,c) for(int i=(a);i<(b);i+=(c))
#define rep(...) overload4(__VA_ARGS__,rep4,rep3,rep2,rep1)(__VA_ARGS__)
#define rrep1(n) for(int i=(n)-1;i>=0;--i)
#define rrep2(i,n) for(int i=(n)-1;i>=0;--i)
#define rrep3(i,a,b) for(int i=(b)-1;i>=a;--i)
#define rrep4(i,a,b,c) for(int i=((b)-(a)-1)/(c)*(c)+(a);i>=a;i-=c)
#define rrep(...) overload4(__VA_ARGS__,rrep4,rrep3,rrep2,rrep1)(__VA_ARGS__)

#define eb emplace_back
#define pb push_back
#define pob pop_back
#define puf push_front
#define pof pop_front

#define mp make_pair
#define mt make_tuple
#define fat forward_as_tuple

#define fi first
#define sd second

#define all(a) begin(a), end(a)
#define rall(a) rbegin(a), rend(a)

#define elif else if
#define unless(a) if(!(a))
#define qt(a) #a

#define endl '\n'
#define deci(n) fixed << setprecision(n)
#define zfl(n) setfill(‘0’) << setw(n)

template<typename T=int, class C> constexpr T sz(const C& a) { return (T)a.size(); }
template<class C> constexpr C& rev(C& a) { reverse(begin(a), end(a)); return a; }
template<class C> constexpr C rev(C&& a) { return forward<C>(rev(a)); }
template<class C> constexpr C& sort(C& a) { sort(begin(a), end(a)); return a; }
template<class C> constexpr C sort(C&& a) { return forward<C>(sort(a)); }
template<class C> constexpr bool np(C& a) { return next_permutation(begin(a), end(a)); }
template<class C> constexpr bool pp(C& a) { return prev_permutation(begin(a), end(a)); }
template<class C, typename T> constexpr bool bs(C const& a, T const& v) { return binary_search(begin(a), end(a), v); }
template<class C, typename T> constexpr auto lb(C const& a, T const& v) { return lower_bound(begin(a), end(a), v); }
template<class C, typename T> constexpr auto ub(C const& a, T const& v) { return upper_bound(begin(a), end(a), v); }
template<class C, class F> constexpr auto bd(C const& a, F const& f) { return partition_point(begin(a), end(a), f); } // boundary
template<class C> constexpr auto& min(C const& a) { return *min_element(begin(a), end(a)); }
template<class C> constexpr auto& max(C const& a) { return *max_element(begin(a), end(a)); }
template<class T> constexpr void rs(vector<T>& a, size_t n) { a.resize(n); }
template<class T> constexpr void rsv(vector<T>& a, size_t n) { a.reserve(n); }
template<class C> constexpr auto total(C const& c) { return accumulate(next(begin(c)), end(c), *begin(c)); }
template<class C, class F> constexpr auto foldl(C const& c, F const& f) { return accumulate(next(begin(c)), end(c), *begin(c), f); }
template<class C> constexpr auto RLE(C const& v) { // run length encoding
  using T = typename C::value_type; using U = typename C::size_type;
  vector<pair<T, U>> w{}; auto t=begin(v); U cnt=0;
  for(auto i=begin(v); i!=end(v); ++i) { if(*t == *i) ++cnt; else w.emplace_back(*t, cnt), t=i, cnt=1; }
  if(cnt > 0) w.emplace_back(*t, cnt); return w;
}

template<typename T> constexpr T gcd2(T a, T b) { while(b) { T t=b; b=a%b; a=t; } return a; }
template<typename T> constexpr T lcm2(T a, T b) { return a/gcd2(a,b)*b; }
template<typename T> constexpr T extgcd2(T a, T b, T& x, T& y) { x=1; y=0; while(b) { T t=a/b; T u=b; b=a-t*b; a=u; u=y; y=x-t*y; x=u; } return a; }

template<typename T> constexpr bool chmax(T& a, T const& b) { if (a < b) { a = b; return true; } return false; }
template<typename T> constexpr bool chmin(T& a, T const& b) { if (b < a) { a = b; return true; } return false; }

template<typename T, typename U> constexpr T ceil(T const& A, U const& B) { return (A+B-1) / B; } // ceil(A/B)

#define mv(T,...) make_v<T>(__VA_ARGS__)
template<typename T> constexpr auto make_v() { return vector<T>(); } template<typename T> constexpr auto make_v(size_t a) { return vector<T>(a); }
template<typename T, typename... Ts> constexpr auto make_v(size_t a, Ts... ts) { return vector<decltype(make_v<T>(ts...))>(a, make_v<T>(ts...)); }

template<typename T> struct has_itr {
  template<typename U> static auto check(U x) -> decltype(begin(x), end(x), true_type()); static false_type check(...);
  static bool constexpr value = decltype(check(declval<T>()))::value;
};
template<typename T, typename V, enable_if_t<!has_itr<T>::value, nullptr_t> = nullptr> constexpr void fill_v(T& t, V const& v) { t = v; }
template<typename T, typename V, enable_if_t< has_itr<T>::value, nullptr_t> = nullptr> constexpr void fill_v(T& t, V const& v) { for(auto&& e : t) fill_v(e, v); }
template<typename T, class F, enable_if_t<!has_itr<T>::value, nullptr_t> = nullptr> constexpr T& apply(T& t, F const& f) { return t = f(t); }
template<typename T, class F, enable_if_t<!has_itr<T>::value, nullptr_t> = nullptr> constexpr T apply(T&& t, F const& f) { return f(forward<T>(t)); }
template<typename T, class F, enable_if_t< has_itr<T>::value, nullptr_t> = nullptr> constexpr T& apply(T& t, F const& f) { for(auto&& e : t) apply(e, f); return t; }
template<typename T, class F, enable_if_t< has_itr<T>::value, nullptr_t> = nullptr> constexpr T apply(T&& t, F const& f) { return forward<T>(apply(t, f)); }

template<typename T, typename U> istream& operator>>(istream& is, pair<T, U>& p) { is >> p.first; is >> p.second; return is; }
template<typename T, typename U> ostream& operator<<(ostream& os, pair<T, U> const& p) { os << p.first << ' ' << p.second; return os; }

template<typename T> istream& operator>>(istream& is, vector<T>& v) { for(auto&& x : v) is >> x; return is; }
template<typename T> struct is_string : false_type {}; template<> struct is_string<string> : true_type {};
template<typename T, enable_if_t<has_itr<T>::value && !is_string<T>::value, nullptr_t> = nullptr> ostream& operator<<(ostream& os, T const& t)
{ for(auto i=begin(t);i!=end(t);++i) { if(i!=begin(t)) os<<' '; os<<*i; } return os; }

template<typename T> struct is_pair : false_type {}; template<typename T, typename U> struct is_pair<pair<T, U>> : true_type {};
template<typename T, enable_if_t<!is_pair<T>::value, nullptr_t> = nullptr> constexpr T& dcr(T& t) { return --t; }
template<typename T, enable_if_t< is_pair<T>::value, nullptr_t> = nullptr> constexpr T& dcr(T& t) { --t.ft; --t.sd; return t; }

template<typename T> struct is_priority_queue : false_type {}; template<class T, class V, class C> struct is_priority_queue<priority_queue<T, V, C>> : true_type {};
template<typename T, enable_if_t< is_priority_queue<T>::value, nullptr_t> = nullptr> constexpr auto pop_front(T& p) { auto q = p.top(); p.pop(); return q; }

struct InitIO {
  InitIO() {
    cin.tie(nullptr); cout.tie(nullptr); cerr.tie(nullptr);
    ios_base::sync_with_stdio(false);
    cout << fixed << setprecision(15) << boolalpha;
    cerr << fixed << setprecision(15) << boolalpha;
    #ifndef LOCAL
      cerr.rdbuf(nullptr);
    #endif
  }
} initio;
#ifdef LOCAL
  #include <boost/type_index.hpp>
  #define trace(...) Trace(" " #__VA_ARGS__ ",", __VA_ARGS__)
  template<typename T> inline string type_name() { return boost::typeindex::type_id_with_cvr<T>().pretty_name(); }
  template<typename T> inline char const* Trace(char const* n, T const& t) {
    auto c = strchr(n+1, ','); cerr.write(n, c-n) << " = " << t << "; // " << type_name<T>() << "\n"; return c; }
  template<typename T, typename... Ts> inline char const* Trace(char const* n, T const& t, Ts const&... ts) {
    char const* c = Trace(n, t); return Trace(c+1, ts...); }
#else
  #define trace(...)
#endif

#define in(T,...) In<T>(__VA_ARGS__)
template<typename T> inline T& sc(T& x) { cin >> x; return x; }
template<typename T, typename... Ts> inline void sc(T& t, Ts&... ts) { sc(t); sc(ts...); }
template<typename T=int> inline T In() { T x; sc(x); return x; }
template<typename T, typename... Ts> inline auto In(Ts ...ts) { auto v = make_v<T>(ts...); sc(v); return v; }
inline string input() { string str; cin >> ws; getline(cin, str); return str; }
template<typename T> inline void out(T const& x) { cout << x; }
template<typename T, typename... Ts> inline void out(T const& t, Ts const&... ts) { out(t); out(' '); out(ts...); }
template<typename T> inline void err(T const& x) { cerr << x; }
template<typename T, typename... Ts> inline void err(T const& t, Ts const&... ts) { err(t); err(' '); err(ts...); }
template<typename... Ts> inline void print(Ts const&... ts) { out(ts...); out('\n'); }
template<typename... Ts> inline void debug(Ts const&... ts) { err(ts...); err('\n'); }

constexpr auto yes(bool const& c) { return c ? "yes" : "no"; }
constexpr auto Yes(bool const& c) { return c ? "Yes" : "No"; }
constexpr auto YES(bool const& c) { return c ? "YES" : "NO"; }
constexpr auto pos(bool const& c) { return c ? "possible" : "impossible"; }
constexpr auto Pos(bool const& c) { return c ? "Possible" : "Impossible"; }
constexpr auto POS(bool const& c) { return c ? "POSSIBLE" : "IMPOSSIBLE"; }

namespace Modint {
  using Int = std::int_fast64_t;
  constexpr Int MOD = 1e9+7;

  // P : prime
  template<Int P=MOD> struct Zp {
    static constexpr Int mod = P;
    Int n;
    constexpr Zp() noexcept : n(0) {}
    template<typename T> constexpr Zp(T const& m) noexcept : n(0) { set(m); }
    constexpr Int const& get() const noexcept { return n; }
    template<typename T> constexpr void set(T const& m) noexcept { n = cast(m); }
    template<typename T> static constexpr Int cast(T const& m) noexcept { Int v=(Int)m; v%=mod; if(v<0) v+=mod; return v; }
    static constexpr Int cast(Zp const& m) noexcept { return m.get(); }
    static constexpr Int extgcd(Int a, Int b, Int& x, Int& y) noexcept { x=1; y=0; while(b) { Int t=a/b; Int u=b; b=a-t*b; a=u; u=y; y=x-t*y; x=u; } return a; }
    template<typename T> static constexpr Zp inv(T const& m) noexcept { Int x=1, y=0; Int d = extgcd((Int)m, mod, x, y); return Zp(d==1 ? x : 0); }
    template<typename T> constexpr Zp& operator=(T const& m) noexcept { set(m); return *this; }
    template<typename T> constexpr Zp& operator+=(T const& m) noexcept { set( n + cast(m) ); return *this; }
    template<typename T> constexpr Zp& operator-=(T const& m) noexcept { set( n - cast(m) ); return *this; }
    template<typename T> constexpr Zp& operator*=(T const& m) noexcept { set( n * cast(m) ); return *this; }
    template<typename T> constexpr Zp& operator/=(T const& m) noexcept { set( n * inv(cast(m)) ); return *this; }
    template<typename T> constexpr Zp pow(T e) const noexcept { if(e==0) return Zp(1); else if(e < 0) return inv(pow(-e)); else { Zp a=*this, r=1; while(e>0){ if(e&1) r*=a; a*=a; e>>=1; } return r; } }
    template<typename T> explicit constexpr operator T() const noexcept { return (T)n; }
    constexpr Zp operator+() const noexcept { return *this; } constexpr Zp operator-() const noexcept { return Zp(-n); }
    template<typename T, typename U> friend constexpr Zp operator+(T const& n, U const& m) noexcept { return Zp(n) += m; }
    template<typename T, typename U> friend constexpr Zp operator-(T const& n, U const& m) noexcept { return Zp(n) -= m; }
    template<typename T, typename U> friend constexpr Zp operator*(T const& n, U const& m) noexcept { return Zp(n) *= m; }
    template<typename T, typename U> friend constexpr Zp operator/(T const& n, U const& m) noexcept { return Zp(n) /= m; }
    template<typename T, typename U> friend constexpr bool operator==(T const& n, U const& m) noexcept { return Zp(m).get() == Zp(n).get(); }
    template<typename T, typename U> friend constexpr bool operator!=(T const& n, U const& m) noexcept { return !(n == m); }
    friend std::istream& operator>>(std::istream& is, Zp& n) noexcept { Int m=0; is >> m; n = m; return is; }
    friend std::ostream& operator<<(std::ostream& os, Zp const& n) noexcept { os << n.get(); return os; }
  };
}
using Mod = Modint::Zp<998244353>;

constexpr int lim = 1e5+7;

signed main() {

  let N = in();
  let D = in(int, N);

  auto E = D;
  sort(E);
  var F = RLE(E);

  if(D[0] != 0 || F[0].second > 1) {
    print(0);
    return 0;
  }

  Mod cnt = 1;

  //trace(F);
  rep(i, 1, sz(F)) {
    if(F[i].first > F[i-1].first+1 ) {
      cnt = 0;
      break;
    }

    cnt *= Mod(F[i-1].second).pow(F[i].second);
  }
  print(cnt);

  return 0;
}


Measured memory_bytes: 3820