Problem:
Score : 600 points

Problem Statement

There are 2N people numbered 1 through 2N.
The height of Person i is h_i.

How many ways are there to make N pairs of people such that the following conditions are satisfied?
Compute the answer modulo 998,244,353.

Each person is contained in exactly one pair.

For each pair, the heights of the two people in the pair are different.

Two ways are considered different if for some p and q, Person p and Person q are paired in one way and not in the other.

Constraints

1 \leq N \leq 50,000

1 \leq h_i \leq 100,000

All values in input are integers.

Input

Input is given from Standard Input in the following format:

N
h_1
:
h_{2N}

Output

Print the answer.

Sample Input 1

2
1
1
2
3

Sample Output 1

2

There are two ways:

Form the pair (Person 1, Person 3) and the pair (Person 2, Person 4).

Form the pair (Person 1, Person 4) and the pair (Person 2, Person 3).

Sample Input 2

5
30
10
20
40
20
10
10
30
50
60

Sample Output 2

516

Solution in Rust (1.42.0) (judge verdict: Accepted)

// ---------- begin ModInt ----------
mod modint {

    #[allow(dead_code)]
    pub struct Mod;
    impl ConstantModulo for Mod {
        const MOD: u32 = 1_000_000_007;
    }

    #[allow(dead_code)]
    pub struct StaticMod;
    static mut STATIC_MOD: u32 = 0;
    impl Modulo for StaticMod {
        fn modulo() -> u32 {
            unsafe { STATIC_MOD }
        }
    }

    #[allow(dead_code)]
    impl StaticMod {
        pub fn set_modulo(p: u32) {
            unsafe {
                STATIC_MOD = p;
            }
        }
    }

    use std::marker::*;
    use std::ops::*;

    pub trait Modulo {
        fn modulo() -> u32;
    }

    pub trait ConstantModulo {
        const MOD: u32;
    }

    impl<T> Modulo for T
    where
        T: ConstantModulo,
    {
        fn modulo() -> u32 {
            T::MOD
        }
    }

    pub struct ModInt<T>(pub u32, PhantomData<T>);

    impl<T> Clone for ModInt<T> {
        fn clone(&self) -> Self {
            ModInt::new_unchecked(self.0)
        }
    }

    impl<T> Copy for ModInt<T> {}

    impl<T: Modulo> Add for ModInt<T> {
        type Output = ModInt<T>;
        fn add(self, rhs: Self) -> Self::Output {
            let mut d = self.0 + rhs.0;
            if d >= T::modulo() {
                d -= T::modulo();
            }
            ModInt::new_unchecked(d)
        }
    }

    impl<T: Modulo> AddAssign for ModInt<T> {
        fn add_assign(&mut self, rhs: Self) {
            *self = *self + rhs;
        }
    }

    impl<T: Modulo> Sub for ModInt<T> {
        type Output = ModInt<T>;
        fn sub(self, rhs: Self) -> Self::Output {
            let mut d = T::modulo() + self.0 - rhs.0;
            if d >= T::modulo() {
                d -= T::modulo();
            }
            ModInt::new_unchecked(d)
        }
    }

    impl<T: Modulo> SubAssign for ModInt<T> {
        fn sub_assign(&mut self, rhs: Self) {
            *self = *self - rhs;
        }
    }

    impl<T: Modulo> Mul for ModInt<T> {
        type Output = ModInt<T>;
        fn mul(self, rhs: Self) -> Self::Output {
            let v = self.0 as u64 * rhs.0 as u64 % T::modulo() as u64;
            ModInt::new_unchecked(v as u32)
        }
    }

    impl<T: Modulo> MulAssign for ModInt<T> {
        fn mul_assign(&mut self, rhs: Self) {
            *self = *self * rhs;
        }
    }

    impl<T: Modulo> Neg for ModInt<T> {
        type Output = ModInt<T>;
        fn neg(self) -> Self::Output {
            if self.0 == 0 {
                Self::zero()
            } else {
                Self::new_unchecked(T::modulo() - self.0)
            }
        }
    }

    impl<T> std::fmt::Display for ModInt<T> {
        fn fmt<'a>(&self, f: &mut std::fmt::Formatter<'a>) -> std::fmt::Result {
            write!(f, "{}", self.0)
        }
    }

    impl<T: Modulo> std::str::FromStr for ModInt<T> {
        type Err = std::num::ParseIntError;
        fn from_str(s: &str) -> Result<Self, Self::Err> {
            let val = s.parse::<u32>()?;
            Ok(ModInt::new(val))
        }
    }

    impl<T: Modulo> From<usize> for ModInt<T> {
        fn from(val: usize) -> ModInt<T> {
            ModInt::new_unchecked((val % T::modulo() as usize) as u32)
        }
    }

    impl<T: Modulo> From<u64> for ModInt<T> {
        fn from(val: u64) -> ModInt<T> {
            ModInt::new_unchecked((val % T::modulo() as u64) as u32)
        }
    }

    impl<T: Modulo> From<i64> for ModInt<T> {
        fn from(val: i64) -> ModInt<T> {
            let m = T::modulo() as i64;
            ModInt::new((val % m + m) as u32)
        }
    }

    #[allow(dead_code)]
    impl<T> ModInt<T> {
        pub fn new_unchecked(d: u32) -> Self {
            ModInt(d, PhantomData)
        }
        pub fn zero() -> Self {
            ModInt::new_unchecked(0)
        }
        pub fn one() -> Self {
            ModInt::new_unchecked(1)
        }
        pub fn is_zero(&self) -> bool {
            self.0 == 0
        }
    }

    #[allow(dead_code)]
    impl<T: Modulo> ModInt<T> {
        pub fn new(d: u32) -> Self {
            ModInt::new_unchecked(d % T::modulo())
        }
        pub fn pow(&self, mut n: u64) -> Self {
            let mut t = Self::one();
            let mut s = *self;
            while n > 0 {
                if n & 1 == 1 {
                    t *= s;
                }
                s *= s;
                n >>= 1;
            }
            t
        }
        pub fn inv(&self) -> Self {
            assert!(self.0 != 0);
            self.pow(T::modulo() as u64 - 2)
        }
    }

    #[allow(dead_code)]
    pub fn mod_pow(r: u64, mut n: u64, m: u64) -> u64 {
        let mut t = 1 % m;
        let mut s = r % m;
        while n > 0 {
            if n & 1 == 1 {
                t = t * s % m;
            }
            s = s * s % m;
            n >>= 1;
        }
        t
    }
}
// ---------- end ModInt ----------
// ---------- begin Precalc ----------
mod precalc {
    use super::modint::*;
    #[allow(dead_code)]
    pub struct Precalc<T> {
        inv: Vec<ModInt<T>>,
        fact: Vec<ModInt<T>>,
        ifact: Vec<ModInt<T>>,
    }
    #[allow(dead_code)]
    impl<T: Modulo> Precalc<T> {
        pub fn new(n: usize) -> Precalc<T> {
            let mut inv = vec![ModInt::one(); n + 1];
            let mut fact = vec![ModInt::one(); n + 1];
            let mut ifact = vec![ModInt::one(); n + 1];
            for i in 2..(n + 1) {
                fact[i] = fact[i - 1] * ModInt::new_unchecked(i as u32);
            }
            ifact[n] = fact[n].inv();
            if n > 0 {
                inv[n] = ifact[n] * fact[n - 1];
            }
            for i in (1..n).rev() {
                ifact[i] = ifact[i + 1] * ModInt::new_unchecked((i + 1) as u32);
                inv[i] = ifact[i] * fact[i - 1];
            }
            Precalc {
                inv: inv,
                fact: fact,
                ifact: ifact,
            }
        }
        pub fn inv(&self, n: usize) -> ModInt<T> {
            assert!(n > 0);
            self.inv[n]
        }
        pub fn fact(&self, n: usize) -> ModInt<T> {
            self.fact[n]
        }
        pub fn ifact(&self, n: usize) -> ModInt<T> {
            self.ifact[n]
        }
        pub fn perm(&self, n: usize, k: usize) -> ModInt<T> {
            if k > n {
                return ModInt::zero();
            }
            self.fact[n] * self.ifact[n - k]
        }
        pub fn comb(&self, n: usize, k: usize) -> ModInt<T> {
            if k > n {
                return ModInt::zero();
            }
            self.fact[n] * self.ifact[k] * self.ifact[n - k]
        }
    }
}
// ---------- end Precalc ----------
// ---------- begin NTT ----------
#[allow(dead_code)]
mod transform {
    use super::modint::*;
    pub trait NTTFriendly: ConstantModulo {
        fn order() -> usize;
        fn zeta() -> u32;
    }
    pub fn ntt<T: NTTFriendly>(f: &mut [ModInt<T>]) {
        let n = f.len();
        assert!(n.count_ones() == 1);
        assert!(n <= T::order());
        let len = n.trailing_zeros() as usize;
        let mut zeta = Vec::with_capacity(len);
        let mut r = ModInt::new_unchecked(T::zeta()).pow((T::order() >> len) as u64);
        for _ in 0..len {
            zeta.push(r);
            r = r * r;
        }
        for (k, &z) in zeta.iter().rev().enumerate().rev() {
            let m = 1 << k;
            for f in f.chunks_exact_mut(2 * m) {
                let mut q = ModInt::one();
                let (x, y) = f.split_at_mut(m);
                for (x, y) in x.iter_mut().zip(y.iter_mut()) {
                    let a = *x;
                    let b = *y;
                    *x = a + b;
                    *y = (a - b) * q;
                    q *= z;
                }
            }
        }
    }
    pub fn intt<T: NTTFriendly>(f: &mut [ModInt<T>]) {
        let n = f.len();
        assert!(n.count_ones() == 1);
        assert!(n <= T::order());
        let len = n.trailing_zeros() as usize;
        let mut zeta = Vec::with_capacity(len);
        let mut r = ModInt::new_unchecked(T::zeta()).inv().pow((T::order() >> len) as u64);
        for _ in 0..len {
            zeta.push(r);
            r = r * r;
        }
        for (k, &z) in zeta.iter().rev().enumerate() {
            let m = 1 << k;
            for f in f.chunks_exact_mut(2 * m) {
                let mut q = ModInt::one();
                let (x, y) = f.split_at_mut(m);
                for (x, y) in x.iter_mut().zip(y.iter_mut()) {
                    let a = *x;
                    let b = *y * q;
                    *x = a + b;
                    *y = a - b;
                    q *= z;
                }
            }
        }
        let ik = ModInt::new_unchecked((T::MOD + 1) >> 1).pow(len as u64);
        for f in f.iter_mut() {
            *f *= ik;
        }
    }
    pub fn multiply<T: NTTFriendly>(a: &[ModInt<T>], b: &[ModInt<T>]) -> Vec<ModInt<T>> {
        if a.is_empty() || b.is_empty() {
            return vec![];
        }
        let n = a.len() + b.len() - 1;
        let k = n.next_power_of_two();
        assert!(k <= T::order());
        let mut f = Vec::with_capacity(k);
        let mut g = Vec::with_capacity(k);
        f.extend_from_slice(a);
        f.resize(k, ModInt::zero());
        ntt(&mut f);
        g.extend_from_slice(b);
        g.resize(k, ModInt::zero());
        ntt(&mut g);
        for (f, g) in f.iter_mut().zip(g.iter()) {
            *f *= *g;
        }
        intt(&mut f);
        f.truncate(n);
        f
    }
}
// ---------- end NTT ----------
// ---------- begin polynomial ----------
#[allow(dead_code)]
mod poly {
    use super::modint::*;
    use super::transform;
    pub struct Polynomial<T> {
        pub a: Vec<ModInt<T>>,
    }
    impl<T: Modulo> Clone for Polynomial<T> {
        fn clone(&self) -> Self {
            Polynomial::new(self.a.iter().map(|a| a.clone()).collect())
        }
    }
    impl<T: Modulo> Polynomial<T> {
        pub fn new(a: Vec<ModInt<T>>) -> Self {
            let mut a = Polynomial { a: a };
            a.fix();
            a
        }
        pub fn from_slice(a: &[ModInt<T>]) -> Self {
            let mut b = Vec::with_capacity(a.len());
            b.extend_from_slice(a);
            Self::new(b)
        }
        pub fn zero() -> Self {
            Polynomial::new(vec![])
        }
        pub fn one() -> Self {
            Polynomial::new(vec![ModInt::one()])
        }
        pub fn get(&self, x: usize) -> ModInt<T> {
            self.a.get(x).cloned().unwrap_or(ModInt::zero())
        }
        pub fn len(&self) -> usize {
            self.a.len()
        }
        pub fn reverse(&self, n: usize) -> Self {
            assert!(self.len() >= n);
            let mut a = Vec::with_capacity(n);
            a.extend_from_slice(&self.a);
            a.resize(n, ModInt::zero());
            a.reverse();
            Self::new(a)
        }
        pub fn truncate(&self, n: usize) -> Self {
            let mut b = self.a.clone();
            b.truncate(n);
            Polynomial::new(b)
        }
        pub fn eval(&self, x: ModInt<T>) -> ModInt<T> {
            let mut ans = ModInt::zero();
            for a in self.a.iter().rev() {
                ans = ans * x + *a;
            }
            ans
        }
        pub fn fix(&mut self) {
            while self.a.last().map_or(false, |a| a.is_zero()) {
                self.a.pop();
            }
        }
        pub fn derivative(&self) -> Self {
            if self.len() < 2 {
                return Polynomial::zero();
            }
            let mut b = vec![ModInt::zero(); self.len() - 1];
            for (i, (b, a)) in b.iter_mut().zip(self.a.iter().skip(1)).enumerate() {
                *b = *a * ModInt::from(i + 1);
            }
            Polynomial::new(b)
        }
        pub fn integral(&self) -> Self {
            if self.len() < 1 {
                return Polynomial::zero();
            }
            let mut b = vec![ModInt::zero(); self.len() + 1];
            let mut inv = vec![ModInt::one(); self.len() + 1];
            b[1] = self.a[0];
            for (i, (b, a)) in b[1..].iter_mut().zip(self.a.iter()).enumerate().skip(1) {
                let k = i + 1;
                inv[k] = -inv[T::modulo() as usize % k] * ModInt::from(T::modulo() as usize / k);
                *b = *a * inv[k];
            }
            Polynomial::new(b)
        }
        pub fn add(&self, rhs: &Self) -> Self {
            let mut ans = vec![ModInt::zero(); std::cmp::max(self.a.len(), rhs.a.len())];
            for (ans, a) in ans.iter_mut().zip(self.a.iter()) {
                *ans = *a;
            }
            for (ans, a) in ans.iter_mut().zip(rhs.a.iter()) {
                *ans += *a;
            }
            Polynomial::new(ans)
        }
        pub fn add_assign(&mut self, rhs: &Self) {
            if self.len() < rhs.len() {
                self.a.resize(rhs.len(), ModInt::zero());
            }
            for (a, b) in self.a.iter_mut().zip(rhs.a.iter()) {
                *a += *b;
            }
        }
        pub fn sub(&self, rhs: &Self) -> Self {
            let mut ans = vec![ModInt::zero(); std::cmp::max(self.a.len(), rhs.a.len())];
            for (ans, a) in ans.iter_mut().zip(self.a.iter()) {
                *ans = *a;
            }
            for (ans, a) in ans.iter_mut().zip(rhs.a.iter()) {
                *ans -= *a;
            }
            Polynomial::new(ans)
        }
        pub fn sub_assign(&mut self, rhs: &Self) {
            if self.len() < rhs.len() {
                self.a.resize(rhs.len(), ModInt::zero());
            }
            for (a, b) in self.a.iter_mut().zip(rhs.a.iter()) {
                *a -= *b;
            }
        }
    }
    impl<T: transform::NTTFriendly> Polynomial<T> {
        pub fn mul(&self, rhs: &Self) -> Self {
            Self::new(transform::multiply(&self.a, &rhs.a))
        }
        pub fn inverse(&self, n: usize) -> Self {
            assert!(self.a.len() > 0 && self.a[0].0 > 0);
            let len = n.next_power_of_two();
            assert!(2 * len <= T::order());
            let mut b = Vec::with_capacity(len);
            b.push(self.a[0].inv());
            let mut f = Vec::with_capacity(2 * len);
            let mut g = Vec::with_capacity(2 * len);
            let mut size = 1;
            while b.len() < n {
                size <<= 1;
                f.clear();
                f.extend_from_slice(&b);
                f.resize(2 * size, ModInt::zero());
                g.clear();
                if self.a.len() >= size {
                    g.extend_from_slice(&self.a[..size]);
                } else {
                    g.extend_from_slice(&self.a);
                }
                g.resize(2 * size, ModInt::zero());
                transform::ntt(&mut f);
                transform::ntt(&mut g);
                for (g, f) in g.iter_mut().zip(f.iter()) {
                    *g *= *f * *f;
                }
                transform::intt(&mut g);
                b.resize(size, ModInt::zero());
                for (b, g) in b.iter_mut().zip(g.iter()) {
                    *b = *b + *b - *g;
                }
            }
            b.truncate(n);
            Polynomial::new(b)
        }
        pub fn div_rem(&self, rhs: &Self) -> (Self, Self) {
            let n = self.len();
            let m = rhs.len();
            assert!(m > 0);
            if n < m {
                return (Polynomial::zero(), self.clone());
            }
            let ia = self.reverse(n).truncate(n - m + 1);
            let ib = rhs.reverse(m).inverse(n - m + 1);
            let id = ia.mul(&ib).truncate(n - m + 1);
            let div = id.reverse(n - m + 1);
            let rem = self.sub(&rhs.mul(&div)).truncate(m - 1);
            (div, rem)
        }
        pub fn rem(&self, rhs: &Self) -> Self {
            self.div_rem(rhs).1
        }
        pub fn log(&self, n: usize) -> Self {
            assert!(self.len() > 0 && self.a[0].0 == 1);
            self.derivative()
                .mul(&self.inverse(n))
                .truncate(n - 1)
                .integral()
        }
        pub fn exp(&self, n: usize) -> Self {
            assert!(self.a.get(0).map_or(true, |a| a.is_zero()) && n <= T::order());
            let mut b = Polynomial::new(vec![ModInt::one()]);
            let mut size = 1;
            while size < n {
                size <<= 1;
                let f = b.log(size);
                let f = Polynomial::from_slice(&self.a[..std::cmp::min(self.len(), size)]).sub(&f);
                b = b.add(&b.mul(&f)).truncate(size);
            }
            b.truncate(n)
        }
        pub fn multi_eval(&self, x: &[ModInt<T>]) -> Vec<ModInt<T>> {
            let size = x.len().next_power_of_two();
            let mut seg = vec![Some(Polynomial::one()); 2 * size];
            for (seg, x) in seg[size..].iter_mut().zip(x.iter()) {
                *seg = Some(Polynomial::from_slice(&[-*x, ModInt::one()]));
            }
            for i in (1..size).rev() {
                seg[i] = Some(
                    seg[2 * i]
                        .as_ref()
                        .unwrap()
                        .mul(seg[2 * i + 1].as_ref().unwrap()),
                );
            }
            let mut rem = vec![None; 2 * size];
            rem[1] = Some(self.rem(&seg[1].take().unwrap()));
            for i in 1..size {
                let a = rem[i].take().unwrap();
                rem[2 * i] = Some(a.rem(&seg[2 * i].take().unwrap()));
                rem[2 * i + 1] = Some(a.rem(&seg[2 * i + 1].take().unwrap()));
            }
            let mut ans = Vec::with_capacity(x.len());
            for a in rem[size..].iter_mut().take(x.len()) {
                ans.push(a.take().unwrap().get(0));
            }
            ans
        }
        pub fn interpolation(x: &[ModInt<T>], y: &[ModInt<T>]) -> Self {
            assert!(x.len() > 0 && x.len() == y.len());
            let size = x.len().next_power_of_two();
            let mut p = vec![Polynomial::one(); 2 * size];
            for (p, x) in p[size..].iter_mut().zip(x.iter()) {
                *p = Polynomial::new(vec![-*x, ModInt::one()]);
            }
            for i in (1..size).rev() {
                p[i] = p[2 * i].mul(&p[2 * i + 1]);
            }
            let z = p[1].derivative().multi_eval(x);
            let mut a = vec![Polynomial::zero(); 2 * size];
            for (a, (z, y)) in a[size..].iter_mut().zip(z.iter().zip(y.iter())) {
                *a = Polynomial::new(vec![*y * z.inv()]);
            }
            for i in (1..size).rev() {
                a[i] = a[2 * i]
                    .mul(&p[2 * i + 1])
                    .add(&a[2 * i + 1].mul(&p[2 * i]));
            }
            a.swap_remove(1)
        }
    }
}

// ---------- begin polynomial ----------

use modint::*;

struct P;
impl ConstantModulo for P {
    const MOD: u32 = 998_244_353;
}

impl transform::NTTFriendly for P {
    fn order() -> usize {
        1 << 23
    }
    fn zeta() -> u32 {
        let p = Self::MOD as u64;
        mod_pow(3, (p - 1) >> 23, p) as u32
    }
}

type M = ModInt<P>;

use proconio::*;
use proconio::marker::*;
use std::collections::*;
use std::cmp::*;
use std::ops::Bound::*;

fn calc(a: &[Vec<M>]) -> Vec<M> {
    if a.len() == 1 {
        a[0].clone()
    } else {
        let m = a.len() / 2;
        let (l, r) = a.split_at(m);
        transform::multiply(&calc(l), &calc(&r))
    }
}

#[fastout]
fn run() {
    input! {
        n: usize,
        h: [usize; 2 * n],
    }
    let w = 100_000;
    let mut cnt = vec![0; w + 1];
    for h in h {
        cnt[h] += 1;
    }
    let pc = precalc::Precalc::new(w + 1);
    let mut memo = vec![];
    for c in cnt {
        if c < 2 {
            continue;
        }
        let mut a = vec![M::one()];
        for i in 1.. {
            if i * 2 > c {
                break;
            }
            a.push(pc.comb(c, 2 * i) * pc.fact(2 * i) * M::new(2).inv().pow(i as u64) * pc.ifact(i));
        }
        memo.push(a);
    }
    if memo.is_empty() {
        let ans = pc.fact(2 * n) * pc.inv(2).pow(n as u64) * pc.ifact(n);
        println!("{}", ans);
        return;
    }
    let a = calc(&memo);
    let mut ans = M::zero();
    let mut sign = M::one();
    for (i, a) in a.into_iter().enumerate() {
        ans += sign * a * pc.fact(2 * n - 2 * i) * pc.inv(2).pow((n - i) as u64) * pc.ifact(n - i);
        sign = -sign;
    }
    println!("{}", ans);
}

fn main() {
    run();
}