#include "iostream"
#include "algorithm"
#include "cstring"
#include "cstdio"
#include "cmath"
#include "vector"
#include "map"
#include "set"
#include "queue"
#include "string"

using namespace std;
#define MAXN 3000006
//#define int long long
#define rep( i , a , b ) for (int i = (a), i##end = (b); i <= i##end; ++i)
#define per( i , a , b ) for (int i = (a), i##end = (b); i >= i##end; --i)
#define pii pair<int,int>
#define fi first
#define se second
#define mp make_pair
#define pb push_back
#define eb emplace_back
#define vi vector<int>
#define all( x ) (x).begin() , (x).end()
#define mem( a ) memset( a , 0 , sizeof a )
typedef long long ll;
#define P 998244353
int n , m;

int Pow( int x , int a ) {
	int ret = 1;
	while( a ) {
		if( a & 1 ) ret = ret * 1ll * x % P;
		x = x * 1ll * x % P , a >>= 1;
	}
	return ret;
}

int Wn[2][MAXN];
void getwn( int len ) {
	for( int mid = 1 ; mid < len ; mid <<= 1 ) {
		int w0 = Pow( 3 , ( P - 1 ) / ( mid << 1 ) ) , w1 = Pow( 3 , P - 1 - ( P - 1 ) / ( mid << 1 ) );
		Wn[0][mid] = Wn[1][mid] = 1;
		for( int i = 1 ; i < mid ; ++ i )
			Wn[0][mid + i] = Wn[0][mid + i - 1] * 1ll * w0 % P,
					Wn[1][mid + i] = Wn[1][mid + i - 1] * 1ll * w1 % P;
	}
}
int rev[MAXN];
void getr( int len ) {
	int l = __builtin_ctz( len ) - 1;
	for( int i = 1 ; i < len ; ++ i ) rev[i] = ( rev[i >> 1] >> 1 ) | ( ( i & 1 ) << l );
}
void NTT( vi& A , int len , int typ ) {
	for( int i = 0 ; i < len ; ++ i ) if( i < rev[i] ) swap( A[i] , A[rev[i]] );
	for( int mid = 1 ; mid < len ; mid <<= 1 )
		for( int i = 0 ; i < len ; i += ( mid << 1 ) )
			for( int j = i ; j < i + mid ; ++ j ) {
				int t0 = A[j] , t1 = A[j + mid] * 1ll * Wn[typ][j - i + mid] % P;
				A[j] = ( t0 + t1 > P ? t0 + t1 - P : t0 + t1 ) , A[j + mid] = ( t0 < t1 ? t0 - t1 + P : t0 - t1 );
			}
	if( typ ) for( int i = 0 , iv = Pow( len , P - 2 ) ; i < len ; ++ i ) A[i] = A[i] * 1ll * iv % P;
}
vi mul( vi a , vi b ) {
	int l = a.size() + b.size() - 1 , len = 1;
	while( len <= l ) len <<= 1;
	getr( len );
	a.resize( len ) , b.resize( len );
	vi as( len );
	NTT( a , len , 0 ) , NTT( b , len , 0 );
	rep( i , 0 , len - 1 ) as[i] = a[i] * 1ll * b[i] % P;
	NTT( as , len , 1 );
	as.resize( l );
	return as;
}

vi pol[MAXN]; int mm;
vi getit( int l , int r ) {
	if( l == r ) return pol[l];
	int m = l + r >> 1;
	return mul( getit( l , m ) , getit( m + 1 , r ) );
}

int A[MAXN] , cn[MAXN];
bool cmp( int x , int y ) {
	return x > y;
}

int J[MAXN] , iJ[MAXN] , pw[MAXN] , ipw[MAXN];
int g( int x ) {
	return J[2 * x] * 1ll * iJ[x] % P * ipw[x] % P;
}
int C( int a , int b ) {
	return J[a] * 1ll * iJ[b] % P * iJ[a - b] % P;
}

void solve( ) {
	getwn( 1 << 20 );
	cin >> n; n <<= 1;
	J[0] = iJ[0] = pw[0] = ipw[0] = 1;
	rep( i , 1 , n ) J[i] = J[i - 1] * 1ll * i % P , iJ[i] = Pow( J[i] , P - 2 ) , pw[i] = pw[i - 1] * 2 % P , ipw[i] = Pow( pw[i] , P - 2 );
	vi a;
	int mx = 0;
	rep( i , 1 , n ) scanf("%d",A + i) , ++ cn[A[i]] , mx = max( mx , A[i] );
	rep( i , 1 , mx ) if( cn[i] ) {
		++ mm;
		rep( j , 0 , cn[i] / 2 )
			pol[mm].pb( g( j ) * 1ll * C( cn[i] , 2 * j ) % P );
//		for( int x : pol[mm] ) cout << x << ' ';
//		puts("");
	}
//	cout << g( 2 ) << endl;
	vi re = getit( 1 , mm );
	int res = 0;
	rep( i , 0 , min( n , (int)re.size() ) )
		res = ( res + ( i & 1 ? P - 1 : 1 ) * 1ll * re[i] % P * g( n / 2 - i ) ) % P;
	cout << res << endl;
}

signed main( ) {
//    freopen("input","r",stdin);
//    freopen("fuckout","w",stdout);
//    int T;cin >> T;while( T-- ) solve();
	solve( );
}


Measured memory_bytes: 87664