#pragma region Macros
#pragma GCC optimize("O3")
#pragma GCC target("avx2,avx")
#pragma GCC optimize("unroll-loops")
#include <bits/stdc++.h>
#define ll long long
#define ld long double
#define rep2(i, a, b) for (ll i = a; i <= b; ++i)
#define rep(i, n) for (ll i = 0; i < n; ++i)
#define rep3(i, a, b) for (ll i = a; i >= b; --i)
#define pii pair<int, int>
#define pll pair<ll, ll>
#define pb push_back
#define eb emplace_back
#define vi vector<int>
#define vll vector<ll>
#define vpi vector<pii>
#define vpll vector<pll>
#define overload2(_1, _2, name, ...) name
#define vec(type, name, ...) vector<type> name(__VA_ARGS__)
#define VEC(type, name, size) \
    vector<type> name(size);  \
    IN(name)
#define vv(type, name, h, ...) vector<vector<type>> name(h, vector<type>(__VA_ARGS__))
#define VV(type, name, h, w)                       \
    vector<vector<type>> name(h, vector<type>(w)); \
    IN(name)
#define vvv(type, name, h, w, ...) vector<vector<vector<type>>> name(h, vector<vector<type>>(w, vector<type>(__VA_ARGS__)))
#define vvvv(type, name, a, b, c, ...) \
    vector<vector<vector<vector<type>>>> name(a, vector<vector<vector<type>>>(b, vector<vector<type>>(c, vector<type>(__VA_ARGS__))))
#define fi first
#define se second
#define all(c) begin(c), end(c)
#define lb(c, x) distance((c).begin(), lower_bound(all(c), (x)))
#define ub(c, x) distance((c).begin(), upper_bound(all(c), (x)))
using namespace std;
string YES[2] = {"NO", "YES"};
string Yes[2] = {"No", "Yes"};
string yes[2] = {"no", "yes"};
template <class T>
using pq = priority_queue<T>;
template <class T>
using pqg = priority_queue<T, vector<T>, greater<T>>;
#define si(c) (int)(c).size()
#define INT(...)     \
    int __VA_ARGS__; \
    IN(__VA_ARGS__)
#define LL(...)     \
    ll __VA_ARGS__; \
    IN(__VA_ARGS__)
#define STR(...)        \
    string __VA_ARGS__; \
    IN(__VA_ARGS__)
#define CHR(...)      \
    char __VA_ARGS__; \
    IN(__VA_ARGS__)
#define DBL(...)        \
    double __VA_ARGS__; \
    IN(__VA_ARGS__)
int scan()
{
    return getchar();
}
void scan(int &a) { cin >> a; }
void scan(long long &a) { cin >> a; }
void scan(char &a) { cin >> a; }
void scan(double &a) { cin >> a; }
void scan(string &a) { cin >> a; }
template <class T, class S>
void scan(pair<T, S> &p) { scan(p.first), scan(p.second); }
template <class T>
void scan(vector<T> &);
template <class T>
void scan(vector<T> &a)
{
    for (auto &i : a)
        scan(i);
}
template <class T>
void scan(T &a) { cin >> a; }
void IN() {}
template <class Head, class... Tail>
void IN(Head &head, Tail &... tail)
{
    scan(head);
    IN(tail...);
}
template <class T, class S>
inline bool chmax(T &a, S b)
{
    if (a < b)
    {
        a = b;
        return 1;
    }
    return 0;
}
template <class T, class S>
inline bool chmin(T &a, S b)
{
    if (a > b)
    {
        a = b;
        return 1;
    }
    return 0;
}
vi iota(int n)
{
    vi a(n);
    iota(all(a), 0);
    return a;
}
template <typename T>
vi iota(vector<T> &a, bool greater = false)
{
    vi res(a.size());
    iota(all(res), 0);
    sort(all(res), [&](int i, int j) {
        if (greater)
            return a[i] > a[j];
        return a[i] < a[j];
    });
    return res;
}
#define UNIQUE(x) sort(all(x)), x.erase(unique(all(x)), x.end())
vector<pll> factor(ll x)
{
    vector<pll> ans;
    for (ll i = 2; i * i <= x; i++)
        if (x % i == 0)
        {
            ans.push_back({i, 1});
            while ((x /= i) % i == 0)
                ans.back().second++;
        }
    if (x != 1)
        ans.push_back({x, 1});
    return ans;
}
template <class T>
vector<T> divisor(T x)
{
    vector<T> ans;
    for (T i = 1; i * i <= x; i++)
        if (x % i == 0)
        {
            ans.pb(i);
            if (i * i != x)
                ans.pb(x / i);
        }
    return ans;
}
template <typename T>
void zip(vector<T> &x)
{
    vector<T> y = x;
    sort(all(y));
    for (int i = 0; i < x.size(); ++i)
    {
        x[i] = lb(y, x[i]);
    }
}
int popcount(ll x) { return __builtin_popcountll(x); }
struct Setup_io
{
    Setup_io()
    {
        ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);
        cout << fixed << setprecision(15);
    }
} setup_io;
int in()
{
    int x;
    cin >> x;
    return x;
}
ll lin()
{
    unsigned long long x;
    cin >> x;
    return x;
}

template <typename T>
struct edge
{
    int from, to;
    T cost;
    int id;
    edge(int to, T cost) : from(-1), to(to), cost(cost) {}
    edge(int from, int to, T cost) : from(from), to(to), cost(cost) {}
    edge(int from, int to, T cost, int id) : from(from), to(to), cost(cost), id(id) {}
    edge &operator=(const int &x)
    {
        to = x;
        return *this;
    }
    operator int() const { return to; }
};
template <typename T>
using Edges = vector<edge<T>>;

using Tree = vector<vector<int>>;
using Graph = vector<vector<int>>;
template <class T>
using Wgraph = vector<vector<edge<T>>>;
Graph getG(int n, int m = -1, bool directed = false, int margin = 1)
{
    Tree res(n);
    if (m == -1)
        m = n - 1;
    while (m--)
    {
        int a, b;
        cin >> a >> b;
        a -= margin, b -= margin;
        res[a].emplace_back(b);
        if (!directed)
            res[b].emplace_back(a);
    }
    return move(res);
}
template <class T>
Wgraph<T> getWg(int n, int m = -1, bool directed = false, int margin = 1)
{
    Wgraph<T> res(n);
    if (m == -1)
        m = n - 1;
    while (m--)
    {
        int a, b;
        T c;
        cin >> a >> b >> c;
        a -= margin, b -= margin;
        res[a].emplace_back(b, c);
        if (!directed)
            res[b].emplace_back(a, c);
    }
    return move(res);
}

#define i128 __int128_t
#define ull unsigned long long int
#define TEST        \
    INT(testcases); \
    while (testcases--)
template <class T>
ostream &operator<<(ostream &os, const vector<T> &v)
{
    for (auto &e : v)
        cout << e << " ";
    cout << endl;
    return os;
}
template <class T, class S>
ostream &operator<<(ostream &os, const pair<T, S> &p)
{
    cout << "(" << p.fi << ", " << p.se << ")";
    return os;
}
template <class S, class T>
string to_string(pair<S, T> p) { return "(" + to_string(p.first) + "," + to_string(p.second) + ")"; }
template <class A>
string to_string(A v)
{
    if (v.empty())
        return "{}";
    string ret = "{";
    for (auto &x : v)
        ret += to_string(x) + ",";
    ret.back() = '}';
    return ret;
}

void dump() { cerr << endl; }
template <class Head, class... Tail>
void dump(Head head, Tail... tail)
{
    cerr << to_string(head) << " ";
    dump(tail...);
}
#define endl '\n'
#ifdef _LOCAL
#undef endl
#define debug(x)        \
    cout << #x << ": "; \
    dump(x)
#else
#define debug(x)
#endif
mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());

int rnd(int n) { return uniform_int_distribution<int>(0, n - 1)(rng); }
ll rndll(ll n) { return uniform_int_distribution<ll>(0, n - 1)(rng); }

template <typename T>
static constexpr T inf = numeric_limits<T>::max() / 2;
#pragma endregion

namespace modular
{
    constexpr ll MOD = 998244353;
    const int MAXN = 1100000;
    template <ll Modulus>
    class modint
    {
        using u64 = ll;

    public:
        u64 a;

        constexpr modint(const u64 x = 0) noexcept : a(((x % Modulus) + Modulus) % Modulus) {}
        constexpr u64 &value() noexcept { return a; }
        constexpr const u64 &value() const noexcept { return a; }
        constexpr modint operator+(const modint rhs) const noexcept { return modint(*this) += rhs; }
        constexpr modint operator-(const modint rhs) const noexcept { return modint(*this) -= rhs; }
        constexpr modint operator*(const modint rhs) const noexcept { return modint(*this) *= rhs; }
        template <typename T>
        constexpr modint operator^(T rhs) const noexcept { return modint(*this) ^= rhs; }
        constexpr modint operator-() const noexcept { return modint() - *this; }
        constexpr modint &operator+=(const modint rhs) noexcept
        {
            a += rhs.a;
            if (a >= Modulus)
            {
                a -= Modulus;
            }
            return *this;
        }
        constexpr modint &operator-=(const modint rhs) noexcept
        {
            if (a < rhs.a)
            {
                a += Modulus;
            }
            a -= rhs.a;
            return *this;
        }
        constexpr modint &operator*=(const modint rhs) noexcept
        {
            a = a * rhs.a % Modulus;
            return *this;
        }
        constexpr bool operator==(const modint rhs) const noexcept { return a == rhs.a; }
        template <typename T>
        constexpr modint &operator^=(T n) noexcept
        {
            modint<Modulus> res = 1;
            modint<Modulus> x = a;
            while (n)
            {
                if (n & 1)
                    res *= x;
                x *= x;
                n >>= 1;
            }
            a = res.a;
            return *this;
        }
    };
#define mint modint<MOD>
#define vmint vector<mint>
    vmint Inv{0, 1}, Prd{1, 1}, Invprd{1, 1};
    mint inv(int n)
    {
        if (n > MAXN)
            return mint(n) ^ (MOD - 2);
        if (Inv.size() > n)
            return Inv[n];
        else
        {
            for (int i = Inv.size(); i <= n; ++i)
                Inv.emplace_back(Inv[MOD % i] * (-MOD / i));
            return Inv[n];
        }
    }
    mint inv(mint x) { return inv(x.a); }
    mint prd(int n)
    {
        if (Prd.size() > n)
            return Prd[n];
        else
            for (int i = Prd.size(); i <= n; ++i)
                Prd.emplace_back(Prd[i - 1] * i);
        return Prd[n];
    }
    mint invprd(int n)
    {
        if (Invprd.size() > n)
            return Invprd[n];
        else
            for (int i = Invprd.size(); i <= n; ++i)
                Invprd.emplace_back(Invprd[i - 1] * inv(i));
        return Invprd[n];
    }
    mint modpow(ll a, ll n)
    {
        mint x = a;
        return x ^= n;
    }
    mint operator/(mint l, mint r) { return l * inv(r); }
    mint &operator/=(mint &l, mint r) { return l = l / r; }
    mint C(int a, int b)
    {
        if (a < 0 || b < 0)
            return 0;
        if (a < b)
            return 0;
        return prd(a) * invprd(b) * invprd(a - b);
    }
    mint P(int a, int b)
    {
        if (a < 0 || b < 0)
            return 0;
        if (a < b)
            return 0;
        return prd(a) * invprd(a - b);
    }
    ostream &operator<<(ostream &os, mint a)
    {
        os << a.a;
        return os;
    }
    template <typename T>
    ostream &operator<<(ostream &os, vector<T> a)
    {
        for (auto &e : a)
            os << e << " ";
        return os;
    }
    mint operator*(ll x, mint y) { return y * x; }
    istream &operator>>(istream &is, mint &a)
    {
        ll x;
        is >> x;
        a = x;
        return is;
    }
    mint proot = 3;

    void FMT(vmint &f, const bool is_inv = false)
    {
        const int n = f.size();
        const mint root = is_inv ? inv(proot) : proot;
        vmint g(n);
        for (int b = n >> 1; b > 0; b >>= 1)
        {
            mint a = root ^ ((MOD - 1) / (n / b)), p = 1;
            for (int i = 0; i < n; i += b << 1)
            {
                rep(j, b)
                {
                    f[i + j + b] *= p;
                    g[(i >> 1) + j] = f[i + j] + f[i + b + j];
                    g[(n >> 1) + (i >> 1) + j] = f[i + j] - f[i + b + j];
                }
                p *= a;
            }
            swap(f, g);
        }
        if (is_inv)
            rep(i, n) f[i] *= inv(n);
    }

    vmint mul(vmint x, const vmint &y)
    {
        int n = x.size() + y.size() - 1;
        int s = 1;
        while (s < n)
            s <<= 1;
        x.resize(s);
        FMT(x);
        vmint z(s);
        rep(i, y.size()) z[i] = y[i];
        FMT(z);
        rep(i, s) x[i] *= z[i];
        FMT(x, true);
        x.resize(n);
        return x;
    }

    using Poly = vmint;
    Poly operator-(Poly f)
    {
        for (auto &&e : f)
            e = -e;
        return f;
    }
    Poly &operator+=(Poly &l, const Poly &r)
    {
        l.resize(max(l.size(), r.size()));
        rep(i, r.size()) l[i] += r[i];
        return l;
    }
    Poly operator+(Poly l, const Poly &r) { return l += r; }
    Poly &operator-=(Poly &l, const Poly &r)
    {
        l.resize(max(l.size(), r.size()));
        rep(i, r.size()) l[i] -= r[i];
        return l;
    }
    Poly operator-(Poly l, const Poly &r) { return l -= r; }
    Poly &operator<<=(Poly &f, size_t n) { return f.insert(f.begin(), n, 0), f; }
    Poly operator<<(Poly f, size_t n) { return f <<= n; }
    Poly &operator>>=(Poly &f, size_t n) { return f.erase(f.begin(), f.begin() + min(f.size(), n)), f; }
    Poly operator>>(Poly f, size_t n) { return f >>= n; }
    Poly operator*(const Poly &l, const Poly &r) { return mul(l, r); }
    Poly &operator*=(Poly &l, const Poly &r) { return l = l * r; }
    Poly inv(const Poly &f)
    {
        Poly g{1 / f[0]};
        while (g.size() < f.size())
        {
            Poly x(f.begin(), f.begin() + min(f.size(), g.size() << 1)), y = g;
            x.resize(g.size() << 1), FMT(x);
            y.resize(g.size() << 1), FMT(y);
            rep(i, x.size()) x[i] *= y[i];
            FMT(x, true);
            x >>= g.size();
            x.resize(g.size() << 1), FMT(x);
            rep(i, x.size()) x[i] *= -y[i];
            FMT(x, true);
            g.insert(g.end(), x.begin(), x.begin() + g.size());
        }
        return Poly{begin(g), begin(g) + f.size()};
    }
    Poly integ(const Poly &f)
    {
        Poly res(f.size() + 1);
        for (int i = 1; i < (int)res.size(); ++i)
            res[i] = f[i - 1] / i;
        return res;
    }
    Poly deriv(const Poly &f)
    {
        if (f.size() == 0)
            return Poly();
        Poly res(f.size() - 1);
        rep(i, res.size()) res[i] = f[i + 1] * (i + 1);
        return res;
    }
    Poly log(const Poly &f)
    {
        Poly g = integ(inv(f) * deriv(f));
        return Poly{g.begin(), g.begin() + f.size()};
    }
    Poly exp(const Poly &f)
    {
        Poly g{1};
        while (g.size() < f.size())
        {
            Poly x(f.begin(), f.begin() + min(f.size(), g.size() * 2));
            x[0] += 1;
            g.resize(2 * g.size());
            x -= log(g);
            x *= {g.begin(), g.begin() + g.size() / 2};
            rep2(i, g.size() / 2, min<int>(x.size(), g.size()) - 1) g[i] = x[i];
        }
        return {g.begin(), g.begin() + f.size()};
    }

} // namespace modular
using namespace modular;

mint modpow(mint x, ll n) { return modpow(x.a, n); }
int main()
{
    INT(n);
    map<int, int> mp;
    rep(i, n * 2)
    {
        INT(x);
        mp[x]++;
    }
    vi v;
    for (auto [a, b] : mp)
        v.eb(b);
    vector<vmint> F;
    for (auto &e : v)
    {
        vmint f(e / 2 + 1);
        rep(i, e / 2 + 1)
        {
            f[i] = P(e, i * 2) * modpow(inv(2), i) * inv(P(i, i));
        }
        F.eb(f);
    }
    pqg<pii> p;
    rep(i, si(F)) p.emplace(si(F[i]), i);
    while (size(p) > 1)
    {
        int a = p.top().se;
        p.pop();
        int b = p.top().se;
        p.pop();
        F[a] = F[a] * F[b];
        p.emplace(si(F[a]), a);
    }
    mint ans = 0;
    auto &f = F[p.top().se];
    rep(i, si(f))
    {
        ans += (i & 1 ? -1 : 1) * f[i] * P(n * 2 - i * 2, n * 2 - i * 2) * modpow(inv(2), n - i) * inv(P(n - i, n - i));
    }
    cout << ans << endl;
}

Measured memory_bytes: 33512