Problem:
G: Two Everywhere
問題
長さ $N$ の数列 $A$ があり、最初、その要素はすべて $0$ です。
数列 $A$ に対し、数列 $A$ の全ての要素が $2$ になるまで、すなわち任意の $i (1 \leq i \leq N)$ について $A_i = 2$ となるまで以下の操作を繰り返します。
操作:
相異なる
整数からなる組 $(i,j)(1 \leq i \lt j \leq N)$ を一様ランダムに選ぶ。そのあと、選ばれた整数組 $(i,j)$ に対し、数列 $A$ の要素を
$A_i \leftarrow (A_i+1) \mod 3$
$A_j \leftarrow (A_j+1) \mod 3$
と更新する。
このとき、操作回数の期待値を求めて下さい。
ただし、期待値が $x/y$ であるとき、
$yz \equiv x \mod 998244353$
を満たすような非負整数 $z$ ($0 \leq z < 998244353$) を出力してください。
なお、この問題の制約下において答えは有理数となることが保証されます。
入力形式
$N$
$N$ : 数列 $A$ の長さ
制約
$2 \leq N \leq 22$
入力は全て整数で与えられる
出力形式
答えを一行に出力し、最後に改行を入れてください。
入力例 1
2
出力例 1
2
入力例 2
9
出力例 2
876657257

Submitted solutions:

code_snippet: /**
	author:  shobonvip
	created: 2025.05.08 18:35:13
**/

#include<bits/stdc++.h>
using namespace std;

//* ATCODER
#include<atcoder/all>
using namespace atcoder;
typedef modint998244353 mint;
//*/

/* BOOST MULTIPRECISION
#include<boost/multiprecision/cpp_int.hpp>
using namespace boost::multiprecision;
//*/

typedef long long ll;

#define rep(i, s, n) for (int i = (int)(s); i < (int)(n); i++)
#define rrep(i, s, n) for (int i = (int)(n)-1; i >= (int)(s); i--)
#define all(v) v.begin(), v.end()

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

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

template <typename T> T max(vector<T> &a){
	assert(!a.empty());
	T ret = a[0];
	for (int i=0; i<(int)a.size(); i++) chmax(ret, a[i]);
	return ret;
}

template <typename T> T min(vector<T> &a){
	assert(!a.empty());
	T ret = a[0];
	for (int i=0; i<(int)a.size(); i++) chmin(ret, a[i]);
	return ret;
}

template <typename T> T sum(vector<T> &a){
	T ret = 0;
	for (int i=0; i<(int)a.size(); i++) ret += a[i];
	return ret;
}

int main(){
	ios_base::sync_with_stdio(false);
	cin.tie(NULL);
	
	int n; cin >> n;

	map<tuple<int,int,int>,int> taio;
	vector<tuple<int,int,int>> v;
	int piv = 0;
	rep(i,0,n+1){
		rep(j,0,n+1){
			rep(k,0,n+1){
				if(i+j+k==n) {
					//cout<<i<<' '<<j<<' '<<k<<endl;
					taio[make_tuple(i,j,k)]=piv;
					v.push_back(make_tuple(i,j,k));
					piv++;
				}
			}
		}
	}

	int m = piv;

	vector a(m, vector<mint>(m + 1));
	rep(i,0,m) {
		if (taio[make_tuple(0,0,n)] == i) {
			a[i][i] = 1;
			a[i][m] = 0;
			continue;
		}
		a[i][i] += 1;
		auto [x,y,z]=v[i];
		if (x>=2){
			a[i][taio[make_tuple(x-2,y+2,z)]] -= mint(x*(x-1)/2) / mint(n*(n-1)/2);
		}
		if (y>=2) {
			a[i][taio[make_tuple(x,y-2,z+2)]] -= mint(y*(y-1)/2) / mint(n*(n-1)/2);
		}
		if (z>=2) {
			a[i][taio[make_tuple(x+2,y,z-2)]] -= mint(z*(z-1)/2) / mint(n*(n-1)/2);
		}
		if(x>=1 && y>=1) {
			a[i][taio[make_tuple(x-1,y,z+1)]] -= mint(x*y) / mint(n*(n-1)/2);
		}
		if(y>=1 && z>=1) {
			a[i][taio[make_tuple(x+1,y-1,z)]] -= mint(y*z) / mint(n*(n-1)/2);
		}
		if(z>=1 && x>=1) {
			a[i][taio[make_tuple(x,y+1,z-1)]] -= mint(z*x) / mint(n*(n-1)/2);
		}
		a[i][m] += 1;
	}

	rep(i,0,m) {
		int v = -1;
		int ind = -1;
		rep(j,i,m){
			bool mode = 0;
			rep(x,0,m){
				if(a[j][x]!=0){
					mode=1;
					ind=x;
					break;
				}
			}
			if(mode){
				v=j;
				break;
			}
		}
		if(v==-1)break;
		swap(a[i],a[v]);
		mint keis=mint(1)/a[i][ind];
		rep(x,0,m+1){
			a[i][x]*=keis;
		}
		rep(j,0,m){
			if(i==j)continue;
			mint tar=a[j][ind];
			rep(x,0,m+1){
				a[j][x]-=tar*a[i][x];
			}
		}
	}

	/*
	rep(i,0,m){
		rep(j,0,m+1){
			cout<<a[i][j].val()<<' ';
		}
		cout<<endl;
	}
		*/

	rep(i,0,m){
		if (a[i][taio[make_tuple(n,0,0)]].val() != 0) {
			cout << a[i][m].val() << endl;
			return 0;
		}
	}
}
accuracy: 1
time_ms: 10
memory_kb: 3840
final_rank: 2

code_snippet: #include<bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<int,int>;
using pll = pair<ll,ll>;
using vi = vector<int>;
using vl = vector<ll>;
#define rep3(i,a,b,c) for(ll i=(a);i<(b);i+=(c))
#define rep2(i,a,b) rep3(i,a,b,1)
#define rep1(i,n) rep2(i,0,n)
#define rep0(n) rep1(aaaaa,n)
#define ov4(a,b,c,d,name,...) name
#define rep(...) ov4(__VA_ARGS__,rep3,rep2,rep1,rep0) (__VA_ARGS__)
#define per(i,a,b) for(ll i=(a)-1;i>=(b);i--)
#define fore(e,v) for(auto&& e:v)
#define all(a) begin(a), end(a)
#define sz(a) (int)(size(a))
#define lb(v,x) (lower_bound(all(v),x)-begin(v))
#define eb emplace_back

template<typename T,typename S> bool chmin(T& a,const S& b){
  return a>b?a=b,1:0;
}
template<typename T,typename S> bool chmax(T& a,const S& b){
  return a<b?a=b,1:0;
}
const int INF=1e9+100;
const ll LINF=3e18+100;

#define i128 __int128_t

struct _ {
  _(){
    cin.tie(0)->sync_with_stdio(0),
    cout.tie(0);
  }
} __;

constexpr int mod = 998244353;
struct mint {
   int x;
   mint(ll x_ = 0) : x(x_ % mod) {
      if(x < 0) x += mod;
   }
   mint operator-() {
      auto res = *this;
      res.x = (x ? mod - x : 0);
      return res;
   }
   mint& operator+=(mint r) {
      if((x += r.x) >= mod) x -= mod;
      return *this;
   }
   mint& operator-=(mint r) {
      if((x -= r.x) < 0) x += mod;
      return *this;
   }
   mint& operator*=(mint r) {
      x = 1LL * x * r.x % mod;
      return *this;
   }
   mint& operator/=(mint r) { return *this *= r.inv(); }
   friend mint operator+(mint a, mint b) { return a += b; }
   friend mint operator-(mint a, mint b) { return a -= b; }
   friend mint operator*(mint a, mint b) { return a *= b; }
   friend mint operator/(mint a, mint b) { return a /= b; }
   mint inv() const { return pow(mod - 2); }
   mint pow(ll b) const {
      mint a = *this, c = 1;
      while(b) {
         if(b & 1) c *= a;
         a *= a;
         b >>= 1;
      }
      return c;
   }
};
using vm = vector<mint>;
template<typename T> struct M {
  vector<vector<T>> a;
  int n, m;
  M(int n, int m) : n(n), m(m), a(n, vector<T>(m)) {}
  M(int n = 0) : M<T>(n, n) {}
  vector<T>& operator[](int k) { return a[k]; }
  const vector<T>& operator[](int k) const { return a[k]; }
  static M I(int n) {
     M mat(n);
     rep(i, n) mat[i][i] = 1;
     return mat;
  }
  M& operator+=(const M& b) {
     rep(i, n) rep(j, m)(*this)[i][j] += b[i][j];
     return *this;
  }
  M& operator-=(const M& b) {
     rep(i, n) rep(j, m)(*this)[i][j] -= b[i][j];
     return *this;
  }
  M& operator*=(const M& b) {
     int l = b.m;
     vector c(n, vector<T>(l));
     rep(i, n) rep(j, m) rep(k, l) c[i][k] += (*this)[i][j] * b[j][k];
     a.swap(c);
     return *this;
  }
  M& operator^=(ll k) {
     M b = M::I(n);
     while(k) {
        if(k & 1) b *= *this;
        *this *= *this;
        k >>= 1;
     }
     a.swap(b.a);
     return *this;
  }
  M operator+(const M& b) const { return (M(*this) += b); }
  M operator-(const M& b) const { return (M(*this) -= b); }
  M operator*(const M& b) const { return (M(*this) *= b); }
  M operator^(const M& b) const { return (M(*this) ^= b); }
};
template<typename T> pair<int, T> GaussElimination(M<T>& a, bool LE = false) {
  int n = a.n, m = a.m;
  int rank = 0, je = LE ? m - 1 : m;
  mint det = 1;
  rep(j, je) {
     int idx = -1;
     rep(i, rank, n) {
        if(a[i][j].x) {
           idx = i;
           break;
        }
     }
     if(idx == -1) {
        det = 0;
        continue;
     }
     if(rank != idx) {
        det = -det;
        swap(a[rank], a[idx]);
     }
     det *= a[rank][j];
     if(LE && a[rank][j].x != 1) {
        mint coeff = a[rank][j].inv();
        rep(k, j, m) a[rank][k] *= coeff;
     }
     int is = LE ? 0 : rank + 1;
     rep(i, is, n) {
        if(i == rank) continue;
        if(a[i][j].x) {
           mint coeff = a[i][j] / a[rank][j];
           rep(k, j, m) a[i][k] -= a[rank][k] * coeff;
        }
     }
     rank++;
  }
  return make_pair(rank, det);
}

template<typename T> vector<vector<T>> LinearEquation(M<T> a, vector<T> b) {
  int n = a.n, m = a.m;
  rep(i, n) a[i].eb(b[i]);
  a.m++;
  auto p = GaussElimination(a, true);
  int rank = p.first;
  rep(i, rank, n) {
     if(a[i][m].x != 0) return {};
  }
  vector<vector<T>> res(1, vector<T>(m));
  vi piv(m, -1);
  int j = 0;
  rep(i, rank) {
     while(a[i][j].x == 0) ++j;
     res[0][j] = a[i][m], piv[j] = i;
  }
  rep(j, m) {
     if(piv[j] == -1) {
        vector<T> x(m);
        x[j] = 1;
        rep(k, j) {
           if(piv[k] != -1) x[k] = -a[piv[k]][j];
        }
        res.eb(x);
     }
  }
  return res;
}


int main(){
  int n;cin >> n;
  int N = (n + 1) * (n + 2) / 2;
  if (n == 2) {
    cout << 2 << endl;
    return 0;
  }
  map<pii, int> mp;
  int t = 0;
  rep(a, n+1) {
    rep(b, n+1) {
      if (a + b > n) break;
      mp[pii{a, b}] = t++;
    }
  }

  M<mint> A(N, N);
  vector<mint> B(N);
  mint y = n * (n - 1);
  y = y.inv();  
  // cerr << t << endl;

  auto f = [&](int i, int na, int nb, mint x) {
    if (0 <= na and 0 <= nb and na + nb <= n) {
      int j = mp[pii{na, nb}];
      A[i][j] = -x * y;
    }
  };

  rep(a, n+1) {
    rep(b, n+1) {
      if (a + b > n) break;
      int i = mp[pii{a, b}];
      A[i][i] = 1;
      if (a == 0 and b == 0) {
        B[i] = 0;
        continue;
      }
      
      B[i] = 1;
      int c = n - a - b;
      f(i, a-2, b+2, a * (a - 1));
      f(i, a, b-2, b * (b-1));
      f(i, a+2, b, c * (c - 1));
      f(i, a-1, b, 2 * a * b);
      f(i, a, b+1, 2 * a * c);
      f(i, a+1, b-1, 2 * b * c); 
    }
  } 
  // rep(i, N) {
  //   rep(j, N) cerr << A[i][j].x << " ";
  //   cerr << B[i].x << endl;
  // }
  auto res = LinearEquation(A, B);
  int I = mp[pii{n, 0}];
  // cout << sz(res) << endl;
  // for(auto x : res) {
  //   fore(y, x) cout << y.x << " ";
  //   cout << endl;
  // }
  cout << res[0][I].x << endl;
}
accuracy: 1
time_ms: 10
memory_kb: 4224
final_rank: 5

code_snippet: #pragma GCC optimize("Ofast")
#include <iostream>
#include <vector>
#include <algorithm>
#include <map>
#include <queue>
#include <cstdio>
#include <ctime>
#include <assert.h>
#include <chrono>
#include <random>
#include <numeric>
#include <set>
#include <deque>
#include <stack>
#include <sstream>
#include <utility>
#include <cstring>
#include <unordered_map>
#include <unordered_set>
#include <tuple>
#include <array>
#include <bitset>
using namespace std;
typedef long long int ll;
typedef unsigned long long ull;

mt19937_64 rng(chrono::steady_clock::now().time_since_epoch().count());
ll myRand(ll B) {
    return (ull)rng() % B;
}
inline ll time() {
    return static_cast<long double>(chrono::duration_cast<chrono::nanoseconds>(chrono::steady_clock::now().time_since_epoch()).count()) * 1e-9;
}

constexpr ll mod = 998244353;

ll mod_pow(ll a,ll b){
    a%=mod;
    if(b==0)return 1;
    if(b==1)return a;
    ll res=mod_pow(a,b/2)%mod;
    res*=res; res%=mod;
    if(b%2)res*=a;
    return res%mod;
}

struct perm{
private:
    int sz;
    vector<ll> p,invp;
public:
    perm(int n){
        sz=n+1;
        p.resize(sz),invp.resize(sz);
        p[0]=1;
        for(int i=1;i<=sz-1;i++){
            p[i]=p[i-1]*i%mod;
        }
        invp[sz-1]=mod_pow(p[sz-1],mod-2);
        for(int i=sz-2;i>=0;i--){
            invp[i]=invp[i+1]*(i+1)%mod;
        }
    }
    ll comb(ll x,ll y){
        if(x<y||y<0)return 0;
        return (p[x]*invp[x-y]%mod)*invp[y]%mod;
    }
};
perm P(1<<20);


template<typename T> using V = vector<T>;
template<typename T> using VV = vector<vector<T>>;
template<typename T> using PQ = priority_queue<T>;
template<typename T> using PQR = priority_queue<T,vector<T>,greater<T>>;
#define fs first
#define sc second
#define pb push_back
#define mp make_pair
#define mt make_tuple
#define eb emplace_back
#define lb(c, x) distance((c).begin(), lower_bound(all(c), (x)))
#define ub(c, x) distance((c).begin(), upper_bound(all(c), (x)))
#define all(v) (v).begin(),(v).end()
#define siz(v) (ll)(v).size()
#define rep(i,a,n) for(ll i=a;i<(ll)(n);++i)
#define repr(i,a,n) for(ll i=n-1;(ll)a<=i;--i)
#define pw2(n) (1ll<<(n))
#define pcntl(x) __builtin_popcountll(x)
#define ENDL '\n'
typedef pair<int,int> Pi;
typedef pair<ll,ll> PL;

template <const long long MOD = 998244353> // MODを適宜設定すること
struct Mint {
  using M = Mint;
  ll a;
  Mint(ll x = 0) {
    a = x % MOD;
    if (a < 0)
      a += MOD;
  } // Mintで

  M pow(ll n) {
    assert(n >= 0);
    M b = *this, r = 1 % MOD;
    while (n) {
      if (n & 1)
        r *= b;
      b *= b;
      n >>= 1;
    }
    return r;
  }

  M inv() { return pow(MOD - 2); }

  M &operator+=(M r) {
    a += r.a;
    if (a >= MOD)
      a -= MOD;
    return *this;
  }
  M &operator-=(M r) {
    a += MOD - r.a;
    if (a >= MOD)
      a -= MOD;
    return *this;
  }
  M &operator*=(M r) {
    a = 1LL * a * r.a % MOD;
    return *this;
  }
  M &operator/=(M r) { return (*this) *= r.inv(); }
  M operator+(M r) const { return M(a) += r; };
  M operator-(M r) const { return M(a) -= r; };
  M operator*(M r) const { return M(a) *= r; };
  M operator/(M r) const { return M(a) /= r; };
  M operator-() const { return M() - *this; }

  friend ostream &operator<<(ostream &os, const M &r) { return os << r.a; }
};
using M = Mint<998244353>;

// is_extendedがtrueのとき、aはAx=BのAとBを連結した行列
int GaussJordan(VV<M> &a, bool is_extended = false) {
  int m = a.size(), n = a[0].size(), rank = 0;
  assert(m);

  rep(c, 0, n) {
    if (is_extended && c == n - 1)
      break;
    int pivot = -1;
    rep(r, rank, m) {
      if (a[r][c].a != 0) {
        pivot = r;
        break;
      }
    }

    if (pivot == -1)
      continue;

    swap(a[pivot], a[rank]);
    M inv = a[rank][c].inv();
    rep(c2, 0, n) a[rank][c2] *= inv;

    rep(r, 0, m) {
      if (r != rank && a[r][c].a) {
        M fac = a[r][c];
        rep(c2, 0, n) a[r][c2] -= a[rank][c2] * fac;
      }
    }
    ++rank;
  }
  return rank;
}

int linear_equation(VV<M> a, vector<ll> b, vector<ll> &res) {
  int m = a.size(), n = a[0].size();
  VV<M> mat(m, V<M>(n + 1));
  rep(i, 0, m) {
    rep(j, 0, n) mat[i][j] = a[i][j];
    mat[i][n] = b[i];
  }
  int rank = GaussJordan(mat, true);

  rep(r, rank, m) if (mat[r][n].a) return -1;

  // answer
  res.assign(n, 0);
  rep(i, 0, rank) res[i] = mat[i][n].a;
  return rank;
}
/*
-1 : 解なし
rank=nのとき、resに解が入る
rank<nのとき、(n-rank)個の値を決めると解が定まる
*/

using state = array<int,3>;

int main(){
    cin.tie(nullptr);
    ios::sync_with_stdio(false);
    int n; cin >> n;
    vector<state> v;
    map<state,int> mp;
    auto find_id=[&](state x)->bool{
        if(mp.find(x) == mp.end()){
            int sz = mp.size();
            v.push_back(x);
            mp[x] = sz; return true;
        }
        return false;
    };
    vector<pair<pair<int,int>,ll>> edge;
    // 遷移列挙
    {
        queue<int> q;
        state init = {n,0,0};
        mp[init] = 0;
        v.push_back(init);
        q.push(0);
        const ll inv = mod_pow(P.comb(n,2), mod-2);
        auto add1=[&](int x)->int{
            if(x+1 == 3)return 0;
            else return x+1;
        };
        while(q.size()){
            int _ = q.front(); q.pop();
            auto cur = v[_];
            for(int i=0;i<3;i++){
                for(int j=i;j<3;j++){
                    if(i == j and cur[i]<2)continue;
                    if(cur[i]<1 or cur[j]<1)continue;
                    int ni = add1(i);
                    int nj = add1(j);
                    cur[i]--; cur[j]--;
                    cur[ni]++; cur[nj]++;
                    if(find_id(cur)){
                        q.push(mp[cur]);
                    }
                    int nxt = mp[cur];
                    cur[i]++; cur[j]++;
                    cur[ni]--; cur[nj]--;
                    ll r;
                    if(i == j){
                        r = P.comb(cur[i],2);
                    }
                    else{
                        r = (ll)cur[i]*(ll)cur[j];
                    }
                    r *= inv;
                    r %= mod;
                    edge.push_back({{_,nxt},r});
                }
            }
        }
    }
    int goal = mp[{0,0,n}];
    int sz = mp.size();
    vector<vector<M>> A(sz,vector<M>(sz));
    vector<ll> B(sz);
    for(auto e:edge){
        if(e.first.first == goal)continue;
        // cout << e.first.first << " " << e.first.second << " " << e.second << endl;
        A[e.first.first][e.first.second] = -e.second;
    }
    for(int i=0;i<sz;i++){
        A[i][i] = 1;
        B[i] = (goal != i);
    }
    vector<ll> res;
    auto r = linear_equation(A, B, res);
    // ll sum = 0;
    // for(int i=0;i<sz;i++){
    //     cout << res[i] << endl;
    //     sum += res[i];
    // }
    cout << res[0] << endl;
}
accuracy: 1
time_ms: 30
memory_kb: 21052
final_rank: 17

code_snippet: #line 2 "library/KowerKoint/base.hpp"

#ifndef ONLINE_JUDGE
#define _GLIBCXX_DEBUG
#endif

#include <bits/stdc++.h>
using namespace std;

#define REP(i, n) for(int i = 0; i < (int)(n); i++)
#define FOR(i, a, b) for(ll i = a; i < (ll)(b); i++)
#define ALL(a) (a).begin(),(a).end()
#define END(...) { print(__VA_ARGS__); return; }

using VI = vector<int>;
using VVI = vector<VI>;
using VVVI = vector<VVI>;
using ll = long long;
using VL = vector<ll>;
using VVL = vector<VL>;
using VVVL = vector<VVL>;
using VD = vector<double>;
using VVD = vector<VD>;
using VVVD = vector<VVD>;
using VS = vector<string>;
using VVS = vector<VS>;
using VVVS = vector<VVS>;
using VC = vector<char>;
using VVC = vector<VC>;
using VVVC = vector<VVC>;
using P = pair<int, int>;
using VP = vector<P>;
using VVP = vector<VP>;
using VVVP = vector<VVP>;
using LP = pair<ll, ll>;
using VLP = vector<LP>;
using VVLP = vector<VLP>;
using VVVLP = vector<VVLP>;

template <typename T>
using PQ = priority_queue<T>;
template <typename T>
using GPQ = priority_queue<T, vector<T>, greater<T>>;

constexpr int INF = 1001001001;
constexpr ll LINF = 1001001001001001001ll;
constexpr int DX[] = {1, 0, -1, 0};
constexpr int DY[] = {0, 1, 0, -1};

void print() { cout << '\n'; }
template<typename T>
void print(const T &t) { cout << t << '\n'; }
template<typename Head, typename... Tail>
void print(const Head &head, const Tail &... tail) {
    cout << head << ' ';
    print(tail...);
}

#ifdef ONLINE_JUDGE
template<typename... Args>
void dbg(const Args &... args) {}
#else
void dbg() { cerr << '\n'; }
template<typename T>
void dbg(const T &t) { cerr << t << '\n'; }
template<typename Head, typename... Tail>
void dbg(const Head &head, const Tail &... tail) {
    cerr << head << ' ';
    dbg(tail...);
}
#endif

template< typename T1, typename T2 >
ostream &operator<<(ostream &os, const pair< T1, T2 >& p) {
    os << p.first << " " << p.second;
    return os;
}

template< typename T1, typename T2 >
istream &operator>>(istream &is, pair< T1, T2 > &p) {
    is >> p.first >> p.second;
    return is;
}

template< typename T >
ostream &operator<<(ostream &os, const vector< T > &v) {
    for(int i = 0; i < (int) v.size(); i++) {
        os << v[i] << (i + 1 != (int) v.size() ? " " : "");
    }
    return os;
}

template< typename T >
istream &operator>>(istream &is, vector< T > &v) {
    for(T &in : v) is >> in;
    return is;
}

template<typename T>
vector<vector<T>> split(typename vector<T>::const_iterator begin, typename vector<T>::const_iterator end, T val) {
    vector<vector<T>> res;
    vector<T> cur;
    for(auto it = begin; it != end; it++) {
        if(*it == val) {
            res.push_back(cur);
            cur.clear();
        } else cur.push_back(val);
    }
    res.push_back(cur);
    return res;
}

vector<string> split(typename string::const_iterator begin, typename string::const_iterator end, char val) {
    vector<string> res;
    string cur = "";
    for(auto it = begin; it != end; it++) {
        if(*it == val) {
            res.push_back(cur);
            cur.clear();
        } else cur.push_back(val);
    }
    res.push_back(cur);
    return res;
}

template< typename T1, typename T2 >
inline bool chmax(T1 &a, T2 b) { return a < b && (a = b, true); }

template< typename T1, typename T2 >
inline bool chmin(T1 &a, T2 b) { return a > b && (a = b, true); }

template <typename T>
pair<VI, vector<T>> compress(const vector<T> &a) {
    int n = a.size();
    vector<T> x;
    REP(i, n) x.push_back(a[i]);
    sort(ALL(x)); x.erase(unique(ALL(x)), x.end());
    VI res(n);
    REP(i, n) res[i] = lower_bound(ALL(x), a[i]) - x.begin();
    return make_pair(res, x);
}

template <typename T>
pair<vector<T>, vector<T>> factorial(int n) {
    vector<T> res(n+1), rev(n+1);
    res[0] = 1;
    REP(i, n) res[i+1] = res[i] * (i+1);
    rev[n] = 1 / res[n];
    for(int i = n; i > 0; i--) {
        rev[i-1] = rev[i] * i;
    }
    return make_pair(res, rev);
}
#line 1 "library/KowerKoint/internal_operator.hpp"
namespace internal_operator {
    template <typename T>
    T default_add(T a, T b) { return a + b; }
    template <typename T>
    T default_sub(T a, T b) { return a - b; }
    template <typename T>
    T zero() { return T(0); }
    template <typename T>
    T default_div(T a, T b) { return a / b; }
    template <typename T>
    T default_mult(T a, T b) { return a * b; }
    template <typename T>
    T one() { return T(1); }
    template <typename T>
    T default_xor(T a, T b) { return a ^ b; }
    template <typename T>
    T default_and(T a, T b) { return a & b; }
    template <typename T>
    T default_or(T a, T b) { return a | b; }
}

#line 4 "library/KowerKoint/math/matrix.hpp"

template <
    typename T,
    T (*add)(const T, const T)=internal_operator::default_add,
    T (*zero)()=internal_operator::zero,
    T (*mult)(const T, const T)=internal_operator::default_mult,
    T (*one)()=internal_operator::one,
    T (*sub)(const T, const T)=internal_operator::default_sub,
    T (*div)(const T, const T)=internal_operator::default_div
>
struct Matrix {
    int n, m;
    vector<vector<T>> A;

    Matrix() : n(0), m(0), A(vector<vector<T>>(0)) {}
    Matrix(size_t _n, size_t _m) : n(_n), m(_m), A(_n, vector<T>(_m, zero())) {}
    Matrix(vector<vector<T>> _A) : n(_A.size()), m(_A[0].size()), A(_A) {}

    vector<T> &operator[](int i) { return A.at(i); }
    const vector<T> &operator[](int i) const { return A.at(i); }

    static Matrix I(size_t n) {
        Matrix ret(n, n);
        REP(i, n) ret[i][i] = one();
        return ret;
    }

    Matrix &operator+=(const Matrix &B) {
        assert(n == B.n && m == B.m);
        REP(i, n) REP(j, m) A[i][j] = add(A[i][j], B[i][j]);
        return *this;
    }
    Matrix operator+(const Matrix &B) const {
        return (Matrix(*this) += B);
    }

    Matrix &operator-=(const Matrix &B) {
        assert(n == B.n && m == B.m);
        REP(i, n) REP(j, m) A[i][j] = sub(A[i][j], B[i][j]);
        return *this;
    }
    Matrix operator-(const Matrix &B) const {
        return (Matrix(*this) -= B);
    }

    Matrix &operator*=(const Matrix &B) {
        assert(m == B.n);
        vector<vector<T>> res(n, vector<T>(B.m, zero()));
        REP(i, n) REP(j, m) REP(k, B.m) res[i][k] = add(res[i][k], mult(A[i][j], B[j][k]));
        A.swap(res);
        m = B.m;
        return (*this);
    }
    Matrix operator*(const Matrix &B) const {
        return (Matrix(*this) *= B);
    }

    Matrix &operator|=(const Matrix &B) {
        assert(B.n == n);
        REP(i, n) {
            A[i].resize(m+B.m);
            REP(j, B.m) A[i][m+j] = B[i][j];
        }
        m += B.m;
        return (*this);
    }
    Matrix operator|(const Matrix &B) const {
        return (Matrix(*this) |= B);
    }

    Matrix &operator|=(const vector<T> &B) {
        assert(B.size() == n);
        REP(i, n) {
            A[i].push_back(B[i]);
        }
        m++;
        return (*this);
    }
    Matrix operator|(const vector<T> &B) const {
        return (Matrix(*this) |= B);
    }

    Matrix &operator&=(const Matrix &B) {
        assert(B.m == m);
        A.resize(n+B.n);
        REP(i, B.n) {
            A[n+i] = B[i];
        }
        n += B.n;
        return (*this);
    }
    Matrix operator&(const Matrix &B) const {
        return (Matrix(*this) &= B);
    }

    Matrix &operator&=(const vector<T> &B) {
        assert(B.size() == m);
        A.push_back(B);
        n++;
        return (*this);
    }
    Matrix operator&(const vector<T> &B) const {
        return (Matrix(*this) &= B);
    }

    friend istream &operator>>(istream &is, Matrix &mat) {
        REP(i, mat.n) REP(j, mat.m) is >> mat[i][j];
        return is;
    }

    friend ostream &operator<<(ostream &os, const Matrix &mat) {
        REP(i, mat.n) {
            REP(j, mat.m) os << mat[i][j] << (j==mat.m-1? '\n' : ' ');
        }
        return os;
    }

    pair<Matrix, T> gaussian_elimination() const {
        Matrix mat(*this);
        T det = one();
        VI columns;
        int i = 0;
        int j = 0;
        while(i < n && j < m) {
            int idx = -1;
            FOR(k, i, n) if(mat[k][j] != zero()) idx = k;
            if(idx == -1) {
                det = zero();
                j++;
                continue;
            }
            if(i != idx) {
                det *= sub(zero(), one());
                swap(mat[i], mat[idx]);
            }
            det *= mat[i][j];
            T scale = mat[i][j];
            REP(l, m) mat[i][l] = div(mat[i][l], scale);
            FOR(k, i+1, n) {
                T scale = mat[k][j];
                REP(l, m) mat[k][l] = sub(mat[k][l], mult(mat[i][l], scale));
            }
            columns.push_back(j);
            i++;
            j++;
        }
        REP(i, columns.size()) {
            int j = columns[i];
            REP(k, i) {
                T scale = mat[k][j];
                FOR(l, j, m) {
                    mat[k][l] = sub(mat[k][l], mult(mat[i][l], scale));
                }
            }
        }
        return make_pair(mat, det);
    }

    void make_basis() {
        *this = gaussian_elimination().first;
        while(n && get_bra(n-1) == vector<T>(m, zero())) pop_bra();
    }

    Matrix inv() const {
        Matrix and_i = A | I(n);
        auto [i_and, det] = and_i.gaussian_elimination();
        assert(det != zero());
        Matrix res(n, n);
        REP(i, n) REP(j, n) res[i][j] = i_and[i][n+i];
        return res;
    }

    vector<T> get_bra(int i) const {
        assert(0 <= i && i < n);
        return A[i];
    }

    vector<T> get_ket(int i) const {
        assert(0 <= i && i < m);
        vector<T> res(n);
        REP(i, n) res[i] = A[i][i];
        return res;
    }

    void pop_bra() {
        assert(n > 0);
        A.pop_back();
        n--;
    }

    void pop_ket() {
        assert(m > 0);
        REP(i, n) A[i].pop_back();
        m--;
    }

    Matrix transpose() const {
        Matrix res(m, n);
        REP(i, n) REP(j, m) res[j][i] = A[i][j];
        return res;
    }

    Matrix operator^=(ll n) {
        if(n < 0) {
            *this = this->inv();
            n = -n;
        }
        Matrix res = Matrix::I(n);
        while(n) {
            if(n & 1) res *= *this;
            *this *= *this;
            n >>= 1LL;
        }
        A.swap(res.A);
        return (*this);
    }
    Matrix operator^(const ll n) const {
        return (Matrix(*this) ^= n);
    }
};

using XorMatrix = Matrix<
    int,
    internal_operator::default_xor<int>,
    internal_operator::zero<int>,
    internal_operator::default_and<int>,
    internal_operator::one<int>,
    internal_operator::default_xor<int>,
    internal_operator::default_and<int>
>;
#line 3 "Contests/Dummy/HUPC2021-day1/g/main.cpp"
constexpr ll mod = 998244353;
#line 1 "library/ei1333/math/combinatorics/mod-int.cpp"
template< int mod >
struct ModInt {
  int x;

  ModInt() : x(0) {}

  ModInt(int64_t y) : x(y >= 0 ? y % mod : (mod - (-y) % mod) % mod) {}

  ModInt &operator+=(const ModInt &p) {
    if((x += p.x) >= mod) x -= mod;
    return *this;
  }

  ModInt &operator-=(const ModInt &p) {
    if((x += mod - p.x) >= mod) x -= mod;
    return *this;
  }

  ModInt &operator*=(const ModInt &p) {
    x = (int) (1LL * x * p.x % mod);
    return *this;
  }

  ModInt &operator/=(const ModInt &p) {
    *this *= p.inverse();
    return *this;
  }

  ModInt operator-() const { return ModInt(-x); }

  ModInt operator+(const ModInt &p) const { return ModInt(*this) += p; }

  ModInt operator-(const ModInt &p) const { return ModInt(*this) -= p; }

  ModInt operator*(const ModInt &p) const { return ModInt(*this) *= p; }

  ModInt operator/(const ModInt &p) const { return ModInt(*this) /= p; }

  bool operator==(const ModInt &p) const { return x == p.x; }

  bool operator!=(const ModInt &p) const { return x != p.x; }

  ModInt inverse() const {
    int a = x, b = mod, u = 1, v = 0, t;
    while(b > 0) {
      t = a / b;
      swap(a -= t * b, b);
      swap(u -= t * v, v);
    }
    return ModInt(u);
  }

  ModInt pow(int64_t n) const {
    ModInt ret(1), mul(x);
    while(n > 0) {
      if(n & 1) ret *= mul;
      mul *= mul;
      n >>= 1;
    }
    return ret;
  }

  friend ostream &operator<<(ostream &os, const ModInt &p) {
    return os << p.x;
  }

  friend istream &operator>>(istream &is, ModInt &a) {
    int64_t t;
    is >> t;
    a = ModInt< mod >(t);
    return (is);
  }

  static int get_mod() { return mod; }
};

using modint = ModInt< mod >;
#line 5 "Contests/Dummy/HUPC2021-day1/g/main.cpp"
using MI3 = ModInt<mod>;

void solve(){
    int n; cin >> n;
    vector<tuple<int, int, int>> v;
    map<tuple<int, int, int>, int> rv;
    stack<tuple<int, int, int>> stk;
    stk.emplace(n, 0, 0);
    v.emplace_back(n, 0, 0);
    rv[{n, 0, 0}] = 0;
    while(!stk.empty()) {
        auto [x, y, z] = stk.top(); stk.pop();
        vector<tuple<int, int, int>> nxt;
        if(x >= 2) nxt.emplace_back(x-2, y+2, z);
        if(y >= 2) nxt.emplace_back(x, y-2, z+2);
        if(z >= 2) nxt.emplace_back(x+2, y, z-2);
        if(x >= 1 && y >= 1) nxt.emplace_back(x-1, y, z+1);
        if(y >= 1 && z >= 1) nxt.emplace_back(x+1, y-1, z);
        if(z >= 1 && x >= 1) nxt.emplace_back(x, y+1, z-1);
        for(auto [nx, ny, nz] : nxt) {
            if(rv.count({nx, ny, nz}) == 0) {
                rv[{nx, ny, nz}] = v.size();
                v.emplace_back(nx, ny, nz);
                stk.emplace(nx, ny, nz);
            }
        }
    }
    int m = v.size();
    Matrix<MI3> mat(m, m+1);
    MI3 al = MI3(n) * MI3(n - 1);
    REP(i, m) {
        auto [x, y, z] = v[i];
        mat[i][i] = 1;
        if(z == n) continue;
        if(x >= 2) {
            mat[i][rv[{x-2, y+2, z}]] -= MI3(x) * MI3(x - 1) / al;
            mat[i][m] += MI3(x) * MI3(x - 1) / al;
        }
        if(y >= 2) {
            mat[i][rv[{x, y-2, z+2}]] -= MI3(y) * MI3(y - 1) / al;
            mat[i][m] += MI3(y) * MI3(y - 1) / al;
        }
        if(z >= 2) {
            mat[i][rv[{x+2, y, z-2}]] -= MI3(z) * MI3(z - 1) / al;
            mat[i][m] += MI3(z) * MI3(z - 1) / al;
        }
        if(x >= 1 && y >= 1) {
            mat[i][rv[{x-1, y, z+1}]] -= MI3(2) * MI3(x) * MI3(y) / al;
            mat[i][m] += MI3(2) * MI3(x) * MI3(y) / al;
        }
        if(y >= 1 && z >= 1) {
            mat[i][rv[{x+1, y-1, z}]] -= MI3(2) * MI3(y) * MI3(z) / al;
            mat[i][m] += MI3(2) * MI3(y) * MI3(z) / al;
        }
        if(z >= 1 && x >= 1) {
            mat[i][rv[{x, y+1, z-1}]] -= MI3(2) * MI3(z) * MI3(x) / al;
            mat[i][m] += MI3(2) * MI3(z) * MI3(x) / al;
        }
    }
    auto [nmat, det] = mat.gaussian_elimination();
    print(nmat[rv[{n, 0, 0}]][m]);
}

// generated by oj-template v4.7.2 (https://github.com/online-judge-tools/template-generator)
int main() {
    // Fasterize input/output script
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout << fixed << setprecision(100);
    // scanf/printf user should delete this fasterize input/output script

    int t = 1;
    //cin >> t; // comment out if solving multi testcase
    for(int testCase = 1;testCase <= t;++testCase){
        solve();
    }
    return 0;
}
accuracy: 1
time_ms: 30
memory_kb: 4008
final_rank: 12

code_snippet: #include <algorithm>
#include <bitset>
#include <complex>
#include <deque>
#include <exception>
#include <fstream>
#include <functional>
#include <iomanip>
#include <ios>
#include <iosfwd>
#include <iostream>
#include <istream>
#include <iterator>
#include <limits>
#include <list>
#include <locale>
#include <map>
#include <memory>
#include <new>
#include <numeric>
#include <ostream>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <stdexcept>
#include <streambuf>
#include <string>
#include <typeinfo>
#include <utility>
#include <valarray>
#include <vector>
#if __cplusplus >= 201103L
#include <array>
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <forward_list>
#include <future>
#include <initializer_list>
#include <mutex>
#include <random>
#include <ratio>
#include <regex>
#include <scoped_allocator>
#include <system_error>
#include <thread>
#include <tuple>
#include <typeindex>
#include <type_traits>
#include <unordered_map>
#include <unordered_set>
#endif
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef unsigned int ui;
typedef pair<int,int> pii;
typedef pair<pii,int> ppii;
typedef pair<int,pii> pipi;
typedef pair<ll,ll> pll;
typedef pair<pll,ll> ppll;
typedef pair<ll,pll> plpl;
typedef pair<pii,pii> pippi;
typedef tuple<ll,ll,ll> tl;
typedef pair<long double,long double> pdd;
typedef vector<vector<ll>> mat;
const ll mod=1000000007;
const ll mod2=998244353;
const ll mod3=1000000009;
ll inf=numeric_limits<ll>::max()/2;
int iinf=numeric_limits<int>::max()/2;
double pi=3.14159265358979323846;
double eps=1e-10;
#define rep(i,m,n) for(ll i=m;i<n;i++)
#define rrep(i,n,m) for(ll i=n;i>=m;i--)
#define srep(itr,st) for(auto itr=st.begin();itr!=st.end();itr++)
#define mrep(itr,mp) for(auto& itr:mp)
#define Max(a,b) a=max(a,b)
#define Min(a,b) a=min(a,b)
int dh[4]={1,0,-1,0};
int dw[4]={0,1,0,-1};
int ddh[8]={-1,-1,-1,0,0,1,1,1};
int ddw[8]={-1,0,1,-1,1,-1,0,1};
struct custom_hash {
    static uint64_t splitmix64(uint64_t x) {
        x+=0x9e3779b97f4a7c15;x=(x^(x>>30))*0xbf58476d1ce4e5b9;x=(x^(x>>27))*0x94d049bb133111eb;
        return x^(x>>31);}
    size_t operator()(uint64_t x) const {
        static const uint64_t FIXED_RANDOM = chrono::steady_clock::now().time_since_epoch().count();
        return splitmix64(x + FIXED_RANDOM);}
};
#define umh unordered_map<int,ll,custom_hash>
ll gcd(ll a,ll b){
    if(a<0)a=-a;if(b<0)b=-b;if(a<b)swap(a,b);if(b==0)return a;if(a%b==0)return b;return gcd(b,a%b);}
ll lcm(ll a,ll b){ll c=gcd(a,b);a/=c;a*=b;return a;}
ll Pow(ll n,ll k){
    if(k<0)return 0;ll ret=1;ll now=n;while(k>0){if(k&1)ret*=now;now*=now;k/=2;}return ret;
}
ll beki(ll n,ll k,ll md){
  ll ret=1;ll now=n;now%=md;
  while(k>0){
    if(k%2==1){ret*=now;ret%=md;}
    now*=now;now%=md;k=k>>1;}return ret;
}
ll gyaku(ll n,ll md){return beki(n,md-2,md);}
ll popcount(ll n){ll ret=0;ll u=n;while(u>0){ret+=u%2;u/=2;}return ret;}
#ifndef ATCODER_INTERNAL_BITOP_HPP
#define ATCODER_INTERNAL_BITOP_HPP 1
#ifdef _MSC_VER
#include <intrin.h>
#endif
namespace atcoder {
namespace internal {
int ceil_pow2(int n) {
    int x = 0;while ((1U << x) < (unsigned int)(n)) x++;return x;}
int bsf(unsigned int n) {
#ifdef _MSC_VER
    unsigned long index;
    _BitScanForward(&index, n);
    return index;
#else
    return __builtin_ctz(n);
#endif
}}}
#endif
#ifndef ATCODER_INTERNAL_MATH_HPP
#define ATCODER_INTERNAL_MATH_HPP 1
#include <utility>
namespace atcoder {
namespace internal {
constexpr long long safe_mod(long long x, long long m) {
    x%=m;if(x<0)x+=m;return x;}
struct barrett {
    unsigned int _m;
    unsigned long long im;
    barrett(unsigned int m) : _m(m), im((unsigned long long)(-1) / m + 1) {}
    unsigned int umod() const { return _m; }
    unsigned int mul(unsigned int a, unsigned int b) const {
        unsigned long long z = a;z *= b;
#ifdef _MSC_VER
        unsigned long long x;
        _umul128(z, im, &x);
#else
        unsigned long long x =
            (unsigned long long)(((unsigned __int128)(z)*im) >> 64);
#endif
        unsigned int v = (unsigned int)(z - x * _m);
        if (_m <= v) v += _m;
        return v;
    }
};
constexpr long long pow_mod_constexpr(long long x, long long n, int m) {
    if (m == 1) return 0;
    unsigned int _m = (unsigned int)(m);
    unsigned long long r = 1;
    unsigned long long y = safe_mod(x, m);
    while (n) {
        if (n & 1) r = (r * y) % _m;y = (y * y) % _m;n >>= 1;
    }
    return r;
}

constexpr bool is_prime_constexpr(int n) {
    if (n <= 1) return false;
    if (n == 2 || n == 7 || n == 61) return true;
    if (n % 2 == 0) return false;
    long long d = n - 1;
    while (d % 2 == 0) d /= 2;
    for (long long a : {2, 7, 61}) {
        long long t = d;
        long long y = pow_mod_constexpr(a, t, n);
        while (t != n - 1 && y != 1 && y != n - 1) {
            y = y * y % n;t <<= 1;}
        if (y != n - 1 && t % 2 == 0) {return false;}
    }
    return true;
}
template <int n> constexpr bool is_prime = is_prime_constexpr(n);
constexpr std::pair<long long, long long> inv_gcd(long long a, long long b) {
    a = safe_mod(a, b);
    if (a == 0) return {b, 0};
    long long s = b, t = a;
    long long m0 = 0, m1 = 1;
    while (t) {
        long long u = s / t;
        s -= t * u;
        m0 -= m1 * u;
        auto tmp = s;
        s = t;
        t = tmp;
        tmp = m0;
        m0 = m1;
        m1 = tmp;
    }
    if (m0 < 0) m0 += b / s;
    return {s, m0};
}
constexpr int primitive_root_constexpr(int m) {
    if (m == 2) return 1;
    if (m == 167772161) return 3;
    if (m == 469762049) return 3;
    if (m == 754974721) return 11;
    if (m == 998244353) return 3;
    int divs[20] = {};
    divs[0] = 2;
    int cnt = 1;
    int x = (m - 1) / 2;
    while (x % 2 == 0) x /= 2;
    for (int i = 3; (long long)(i)*i <= x; i += 2) {
        if (x % i == 0) {
            divs[cnt++] = i;
            while (x % i == 0) {
                x /= i;}
        }
    }
    if (x > 1) {divs[cnt++] = x;}
    for (int g = 2;; g++) {
        bool ok = true;
        for (int i = 0; i < cnt; i++) {
            if (pow_mod_constexpr(g, (m - 1) / divs[i], m) == 1) {
                ok = false;break;
            }
        }
        if (ok) return g;
    }
}
template <int m> constexpr int primitive_root = primitive_root_constexpr(m);
}
}
#endif
#ifndef ATCODER_INTERNAL_QUEUE_HPP
#define ATCODER_INTERNAL_QUEUE_HPP 1
#include <vector>
namespace atcoder {
namespace internal {
template <class T> struct simple_queue {
    std::vector<T> payload;
    int pos = 0;
    void reserve(int n) { payload.reserve(n); }
    int size() const { return int(payload.size()) - pos; }
    bool empty() const { return pos == int(payload.size()); }
    void push(const T& t) { payload.push_back(t); }
    T& front() { return payload[pos]; }
    void clear() {
        payload.clear();pos = 0;}
    void pop() { pos++; }
};
}
}
#endif
#ifndef ATCODER_INTERNAL_SCC_HPP
#define ATCODER_INTERNAL_SCC_HPP 1
#include <algorithm>
#include <utility>
#include <vector>
namespace atcoder {
namespace internal {
template <class E> struct csr {
    std::vector<int> start;
    std::vector<E> elist;
    csr(int n, const std::vector<std::pair<int, E>>& edges)
        : start(n + 1), elist(edges.size()) {
        for (auto e : edges) {start[e.first + 1]++;}
        for (int i = 1; i <= n; i++) {start[i] += start[i - 1];}
        auto counter = start;
        for (auto e : edges) {elist[counter[e.first]++] = e.second;}
    }
};
struct scc_graph {
  public:
    scc_graph(int n) : _n(n) {}
    int num_vertices() { return _n; }
    void add_edge(int from, int to) { edges.push_back({from, {to}}); }
    std::pair<int, std::vector<int>> scc_ids() {
        auto g = csr<edge>(_n, edges);
        int now_ord = 0, group_num = 0;
        std::vector<int> visited, low(_n), ord(_n, -1), ids(_n);
        visited.reserve(_n);
        auto dfs = [&](auto self, int v) -> void {
            low[v] = ord[v] = now_ord++;
            visited.push_back(v);
            for (int i = g.start[v]; i < g.start[v + 1]; i++) {
                auto to = g.elist[i].to;
                if (ord[to] == -1) {
                    self(self, to);low[v] = std::min(low[v], low[to]);
                } else {low[v] = std::min(low[v], ord[to]);}
            }
            if (low[v] == ord[v]) {
                while (true) {
                    int u = visited.back();
                    visited.pop_back();ord[u] = _n;ids[u] = group_num;if (u == v) break;
                }
                group_num++;
            }
        };
        for (int i = 0; i < _n; i++) {
            if (ord[i] == -1) dfs(dfs, i);
        }
        for (auto& x : ids) {
            x = group_num - 1 - x;
        }
        return {group_num, ids};
    }
    std::vector<std::vector<int>> scc() {
        auto ids = scc_ids();
        int group_num = ids.first;
        std::vector<int> counts(group_num);
        for (auto x : ids.second) counts[x]++;
        std::vector<std::vector<int>> groups(ids.first);
        for (int i = 0; i < group_num; i++) {
            groups[i].reserve(counts[i]);
        }
        for (int i = 0; i < _n; i++) {
            groups[ids.second[i]].push_back(i);
        }
        return groups;
    }
  private:
    int _n;
    struct edge {
        int to;
    };
    std::vector<std::pair<int, edge>> edges;
};
}
}
#endif
#ifndef ATCODER_INTERNAL_TYPE_TRAITS_HPP
#define ATCODER_INTERNAL_TYPE_TRAITS_HPP 1
#include <cassert>
#include <numeric>
#include <type_traits>
namespace atcoder {
namespace internal {
#ifndef _MSC_VER
template <class T>
using is_signed_int128 =
    typename std::conditional<std::is_same<T, __int128_t>::value ||
                                  std::is_same<T, __int128>::value,
                              std::true_type,
                              std::false_type>::type;
template <class T>
using is_unsigned_int128 =
    typename std::conditional<std::is_same<T, __uint128_t>::value ||
                                  std::is_same<T, unsigned __int128>::value,
                              std::true_type,
                              std::false_type>::type;
template <class T>
using make_unsigned_int128 =
    typename std::conditional<std::is_same<T, __int128_t>::value,
                              __uint128_t,
                              unsigned __int128>;
template <class T>
using is_integral = typename std::conditional<std::is_integral<T>::value ||
                                                  is_signed_int128<T>::value ||
                                                  is_unsigned_int128<T>::value,
                                              std::true_type,
                                              std::false_type>::type;
template <class T>
using is_signed_int = typename std::conditional<(is_integral<T>::value &&
                                                 std::is_signed<T>::value) ||
                                                    is_signed_int128<T>::value,
                                                std::true_type,
                                                std::false_type>::type;
template <class T>
using is_unsigned_int =
    typename std::conditional<(is_integral<T>::value &&
                               std::is_unsigned<T>::value) ||
                                  is_unsigned_int128<T>::value,
                              std::true_type,
                              std::false_type>::type;
template <class T>
using to_unsigned = typename std::conditional<
    is_signed_int128<T>::value,
    make_unsigned_int128<T>,
    typename std::conditional<std::is_signed<T>::value,
                              std::make_unsigned<T>,
                              std::common_type<T>>::type>::type;
#else
template <class T> using is_integral = typename std::is_integral<T>;
template <class T>
using is_signed_int =
    typename std::conditional<is_integral<T>::value && std::is_signed<T>::value,
                              std::true_type,
                              std::false_type>::type;
template <class T>
using is_unsigned_int =
    typename std::conditional<is_integral<T>::value &&
                                  std::is_unsigned<T>::value,
                              std::true_type,
                              std::false_type>::type;
template <class T>
using to_unsigned = typename std::conditional<is_signed_int<T>::value,
                                              std::make_unsigned<T>,
                                              std::common_type<T>>::type;
#endif
template <class T>
using is_signed_int_t = std::enable_if_t<is_signed_int<T>::value>;
template <class T>
using is_unsigned_int_t = std::enable_if_t<is_unsigned_int<T>::value>;
template <class T> using to_unsigned_t = typename to_unsigned<T>::type;
}
}
#endif
#ifndef ATCODER_MODINT_HPP
#define ATCODER_MODINT_HPP 1
#include <cassert>
#include <numeric>
#include <type_traits>
#ifdef _MSC_VER
#include <intrin.h>
#endif
namespace atcoder {
namespace internal {
struct modint_base {};
struct static_modint_base : modint_base {};
template <class T> using is_modint = std::is_base_of<modint_base, T>;
template <class T> using is_modint_t = std::enable_if_t<is_modint<T>::value>;
}
template <int m, std::enable_if_t<(1 <= m)>* = nullptr>
struct static_modint : internal::static_modint_base {
    using mint = static_modint;
  public:
    static constexpr int mod() { return m; }
    static mint raw(int v) {
        mint x;
        x._v = v;
        return x;
    }
    static_modint() : _v(0) {}
    template <class T, internal::is_signed_int_t<T>* = nullptr>
    static_modint(T v) {
        long long x = (long long)(v % (long long)(umod()));
        if (x < 0) x += umod();
        _v = (unsigned int)(x);
    }
    template <class T, internal::is_unsigned_int_t<T>* = nullptr>
    static_modint(T v) {
        _v = (unsigned int)(v % umod());
    }
    static_modint(bool v) { _v = ((unsigned int)(v) % umod()); }
    unsigned int val() const { return _v; }
    mint& operator++() {
        _v++;
        if (_v == umod()) _v = 0;
        return *this;
    }
    mint& operator--() {
        if (_v == 0) _v = umod();
        _v--;
        return *this;
    }
    mint operator++(int) {
        mint result = *this;
        ++*this;
        return result;
    }
    mint operator--(int) {
        mint result = *this;
        --*this;
        return result;
    }
    mint& operator+=(const mint& rhs) {
        _v += rhs._v;
        if (_v >= umod()) _v -= umod();
        return *this;
    }
    mint& operator-=(const mint& rhs) {
        _v -= rhs._v;
        if (_v >= umod()) _v += umod();
        return *this;
    }
    mint& operator*=(const mint& rhs) {
        unsigned long long z = _v;
        z *= rhs._v;
        _v = (unsigned int)(z % umod());
        return *this;
    }
    mint& operator/=(const mint& rhs) { return *this = *this * rhs.inv(); }
    mint operator+() const { return *this; }
    mint operator-() const { return mint() - *this; }
    mint pow(long long n) const {
        assert(0 <= n);
        mint x = *this, r = 1;
        while (n) {
            if (n & 1) r *= x;
            x *= x;
            n >>= 1;
        }
        return r;
    }
    mint inv() const {
        if (prime) {
            assert(_v);
            return pow(umod() - 2);
        } else {
            auto eg = internal::inv_gcd(_v, m);
            assert(eg.first == 1);
            return eg.second;
        }
    }
    friend mint operator+(const mint& lhs, const mint& rhs) {
        return mint(lhs) += rhs;
    }
    friend mint operator-(const mint& lhs, const mint& rhs) {
        return mint(lhs) -= rhs;
    }
    friend mint operator*(const mint& lhs, const mint& rhs) {
        return mint(lhs) *= rhs;
    }
    friend mint operator/(const mint& lhs, const mint& rhs) {
        return mint(lhs) /= rhs;
    }
    friend bool operator==(const mint& lhs, const mint& rhs) {
        return lhs._v == rhs._v;
    }
    friend bool operator!=(const mint& lhs, const mint& rhs) {
        return lhs._v != rhs._v;
    }
  private:
    unsigned int _v;
    static constexpr unsigned int umod() { return m; }
    static constexpr bool prime = internal::is_prime<m>;
};
template <int id> struct dynamic_modint : internal::modint_base {
    using mint = dynamic_modint;
  public:
    static int mod() { return (int)(bt.umod()); }
    static void set_mod(int m) {
        assert(1 <= m);
        bt = internal::barrett(m);
    }
    static mint raw(int v) {
        mint x;
        x._v = v;
        return x;
    }
    dynamic_modint() : _v(0) {}
    template <class T, internal::is_signed_int_t<T>* = nullptr>
    dynamic_modint(T v) {
        long long x = (long long)(v % (long long)(mod()));
        if (x < 0) x += mod();
        _v = (unsigned int)(x);
    }
    template <class T, internal::is_unsigned_int_t<T>* = nullptr>
    dynamic_modint(T v) {
        _v = (unsigned int)(v % mod());
    }
    dynamic_modint(bool v) { _v = ((unsigned int)(v) % mod()); }
    unsigned int val() const { return _v; }
    mint& operator++() {
        _v++;
        if (_v == umod()) _v = 0;
        return *this;
    }
    mint& operator--() {
        if (_v == 0) _v = umod();
        _v--;
        return *this;
    }
    mint operator++(int) {
        mint result = *this;
        ++*this;
        return result;
    }
    mint operator--(int) {
        mint result = *this;
        --*this;
        return result;
    }
    mint& operator+=(const mint& rhs) {
        _v += rhs._v;
        if (_v >= umod()) _v -= umod();
        return *this;
    }
    mint& operator-=(const mint& rhs) {
        _v += mod() - rhs._v;
        if (_v >= umod()) _v -= umod();
        return *this;
    }
    mint& operator*=(const mint& rhs) {
        _v = bt.mul(_v, rhs._v);
        return *this;
    }
    mint& operator/=(const mint& rhs) { return *this = *this * rhs.inv(); }
    mint operator+() const { return *this; }
    mint operator-() const { return mint() - *this; }
    mint pow(long long n) const {
        assert(0 <= n);
        mint x = *this, r = 1;
        while (n) {
            if (n & 1) r *= x;
            x *= x;
            n >>= 1;
        }
        return r;
    }
    mint inv() const {
        auto eg = internal::inv_gcd(_v, mod());
        assert(eg.first == 1);
        return eg.second;
    }
    friend mint operator+(const mint& lhs, const mint& rhs) {
        return mint(lhs) += rhs;
    }
    friend mint operator-(const mint& lhs, const mint& rhs) {
        return mint(lhs) -= rhs;
    }
    friend mint operator*(const mint& lhs, const mint& rhs) {
        return mint(lhs) *= rhs;
    }
    friend mint operator/(const mint& lhs, const mint& rhs) {
        return mint(lhs) /= rhs;
    }
    friend bool operator==(const mint& lhs, const mint& rhs) {
        return lhs._v == rhs._v;
    }
    friend bool operator!=(const mint& lhs, const mint& rhs) {
        return lhs._v != rhs._v;
    }
  private:
    unsigned int _v;
    static internal::barrett bt;
    static unsigned int umod() { return bt.umod(); }
};
template <int id> internal::barrett dynamic_modint<id>::bt = 998244353;
using modint998244353 = static_modint<998244353>;
using modint1000000007 = static_modint<1000000007>;
using modint = dynamic_modint<-1>;
namespace internal {
template <class T>
using is_static_modint = std::is_base_of<internal::static_modint_base, T>;
template <class T>
using is_static_modint_t = std::enable_if_t<is_static_modint<T>::value>;
template <class> struct is_dynamic_modint : public std::false_type {};
template <int id>
struct is_dynamic_modint<dynamic_modint<id>> : public std::true_type {};
template <class T>
using is_dynamic_modint_t = std::enable_if_t<is_dynamic_modint<T>::value>;
}
}
#endif
#ifndef ATCODER_DSU_HPP
#define ATCODER_DSU_HPP 1
#include <algorithm>
#include <cassert>
#include <vector>
namespace atcoder {

struct dsu {
  public:
    dsu() : _n(0) {}
    dsu(int n) : _n(n), parent_or_size(n, -1) {}
    int merge(int a, int b) {
        assert(0 <= a && a < _n);
        assert(0 <= b && b < _n);
        int x = leader(a), y = leader(b);
        if (x == y) return x;
        if (-parent_or_size[x] < -parent_or_size[y]) std::swap(x, y);
        parent_or_size[x] += parent_or_size[y];
        parent_or_size[y] = x;
        return x;
    }
    bool same(int a, int b) {
        assert(0 <= a && a < _n);
        assert(0 <= b && b < _n);
        return leader(a) == leader(b);
    }
    int leader(int a) {
        assert(0 <= a && a < _n);
        if (parent_or_size[a] < 0) return a;
        return parent_or_size[a] = leader(parent_or_size[a]);
    }
    int size(int a) {
        assert(0 <= a && a < _n);
        return -parent_or_size[leader(a)];
    }
    std::vector<std::vector<int>> groups() {
        std::vector<int> leader_buf(_n), group_size(_n);
        for (int i = 0; i < _n; i++) {
            leader_buf[i] = leader(i);
            group_size[leader_buf[i]]++;
        }
        std::vector<std::vector<int>> result(_n);
        for (int i = 0; i < _n; i++) {
            result[i].reserve(group_size[i]);
        }
        for (int i = 0; i < _n; i++) {
            result[leader_buf[i]].push_back(i);
        }
        result.erase(
            std::remove_if(result.begin(), result.end(),
                           [&](const std::vector<int>& v) { return v.empty(); }),
            result.end());
        return result;
    }
  private:
    int _n;
    
    std::vector<int> parent_or_size;
};
}
#endif
using namespace atcoder;
//#define endl "\n"
using mint=static_modint<mod2>;

vector<mint> GJ(vector<vector<mint>> a,vector<mint> b){
    int n=a.size();
    vector<vector<mint>> B(n,vector<mint>(n+1));
    rep(i,0,n)rep(j,0,n)B[i][j]=a[i][j];
    rep(i,0,n)B[i][n]=b[i];
    rep(i,0,n){
        /*rep(j,0,n){
            rep(k,0,n+1)cout<<B[j][k].val()<<" ";
            cout<<endl;
        }
        cout<<endl;*/
        int pi=i;
        rep(j,i,n){
            if(B[j][i].val()>B[pi][i].val())pi=j;
        }
        swap(B[i],B[pi]);
        if(B[i][i].val()==0)return vector<mint>();
        mint gy=1;
        gy/=B[i][i];
        rep(j,i+1,n+1)(B[i][j]*=gy);
        rep(j,0,n){
            if(i!=j){
                rep(k,i+1,n+1){
                    B[j][k]-=B[j][i]*B[i][k];
                }
            }
        }
    }
    vector<mint> x(n);
    rep(i,0,n)x[i]=B[i][n];
    return x;
}
int main(){
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    int n;cin>>n;
    pii p[(n+1)*(n+1)+1];
    map<pii,int> mp;
    int cnt=0;
    dsu d((n+1)*(n+1));
    rep(i,0,n+1){
        rep(j,0,n+1){
            if(i==0&&j==0)continue;
            if(i+j>n)break;
            rep(ii,0,3){
                rep(jj,ii,3){
                    int n1=i,n2=j,n3=n-i-j;
                    if(ii==0){
                        if(n1==0)continue;
                        if(jj==0){
                            if(n1==1)continue;
                        }
                        else if(jj==1){
                            if(n2==0)continue;
                        }
                        else{
                            if(n3==0)continue;
                        }
                    }
                    if(ii==1){
                        if(n2==0)continue;
                        if(jj==0){
                            if(n1==0)continue;
                        }
                        else if(jj==1){
                            if(n2==1)continue;
                        }
                        else{
                            if(n3==0)continue;
                        }
                    }
                    if(ii==2){
                        if(n3==0)continue;
                        if(jj==0){
                            if(n1==0)continue;
                        }
                        else if(jj==1){
                            if(n2==0)continue;
                        }
                        else{
                            if(n3==1)continue;
                        }
                    }
                    if(ii==0){
                        n1--;
                        n2++;
                    }
                    else if(ii==1){
                        n2--;
                        n3++;
                    }
                    else{
                        n1++;
                        n3--;
                    }
                    if(jj==0){
                        n1--;
                        n2++;
                    }
                    else if(jj==1){
                        n2--;
                        n3++;
                    }
                    else{
                        n1++;
                        n3--;
                    }
                    d.merge(i*(n+1)+j,n1*(n+1)+n2);
                    //cout<<i*(n+1)+j<<" "<<n1*(n+1)+n2<<" "<<ii<<" "<<jj<<endl;
                }
            }
        }
    }
    rep(i,0,n+1){
        rep(j,0,n+1){
            if(i+j>n)break;
            if(d.same(n*(n+1),i*(n+1)+j)==false)continue;
            p[cnt]={i,j};
            //cout<<i<<" "<<j<<endl;
            mp[{i,j}]=cnt;
            cnt++;
        }
    }
    mint y=n*(n-1)/2;
    vector<vector<mint>> v(cnt,vector<mint>(cnt,0));
    vector<mint> b(cnt,0);
    rep(i,0,cnt){
        int num[3];
        num[0]=p[i].first;num[1]=p[i].second;num[2]=n-num[0]-num[1];
        v[i][i]=1;
        if(i==0)continue;
        b[i]=1;
        rep(j,0,3){
            rep(k,j,3){
                mint yy=num[j];
                if(j==k){
                    yy*=num[j]-1;
                    yy/=2;
                }
                else yy*=num[k];
                yy/=y;
                //if(i==2&&j==1&&k==1)cout<<yy.val()<<endl;
                if(yy.val()==0)continue;
                int na=num[0],nb=num[1];
                if(j==0){
                    na--;
                    nb++;
                }
                else if(j==1){
                    nb--;
                }
                else na++;
                if(k==0){
                    na--;
                    nb++;
                }
                else if(k==1){
                    nb--;
                }
                else na++;
                int nex=mp[{na,nb}];
                v[i][nex]-=yy;
            }
        }
    }
    b=GJ(v,b);
    //return 0;
    int numm=mp[{n,0}];
    cout<<b[numm].val()<<endl;
}
accuracy: 1
time_ms: 10
memory_kb: 4068
final_rank: 4

code_snippet: #include <algorithm>
#include <array>
#include <bitset>
#include <cassert>
#include <chrono>
#include <cmath>
#include <complex>
#include <deque>
#include <forward_list>
#include <fstream>
#include <functional>
#include <iomanip>
#include <ios>
#include <iostream>
#include <limits>
#include <list>
#include <map>
#include <numeric>
#include <queue>
#include <random>
#include <set>
#include <sstream>
#include <stack>
#include <string>
#include <tuple>
#include <type_traits>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
using namespace std;
using lint = long long;
using pint = pair<int, int>;
using plint = pair<lint, lint>;
struct fast_ios { fast_ios(){ cin.tie(nullptr), ios::sync_with_stdio(false), cout << fixed << setprecision(20); }; } fast_ios_;
#define ALL(x) (x).begin(), (x).end()
#define FOR(i, begin, end) for(int i=(begin),i##_end_=(end);i<i##_end_;i++)
#define IFOR(i, begin, end) for(int i=(end)-1,i##_begin_=(begin);i>=i##_begin_;i--)
#define REP(i, n) FOR(i,0,n)
#define IREP(i, n) IFOR(i,0,n)
template <typename T, typename V>
void ndarray(vector<T>& vec, const V& val, int len) { vec.assign(len, val); }
template <typename T, typename V, typename... Args> void ndarray(vector<T>& vec, const V& val, int len, Args... args) { vec.resize(len), for_each(begin(vec), end(vec), [&](T& v) { ndarray(v, val, args...); }); }
template <typename T> bool chmax(T &m, const T q) { return m < q ? (m = q, true) : false; }
template <typename T> bool chmin(T &m, const T q) { return m > q ? (m = q, true) : false; }
int floor_lg(long long x) { return x <= 0 ? -1 : 63 - __builtin_clzll(x); }
template <typename T1, typename T2> pair<T1, T2> operator+(const pair<T1, T2> &l, const pair<T1, T2> &r) { return make_pair(l.first + r.first, l.second + r.second); }
template <typename T1, typename T2> pair<T1, T2> operator-(const pair<T1, T2> &l, const pair<T1, T2> &r) { return make_pair(l.first - r.first, l.second - r.second); }
template <typename T> vector<T> sort_unique(vector<T> vec) { sort(vec.begin(), vec.end()), vec.erase(unique(vec.begin(), vec.end()), vec.end()); return vec; }
template <typename T> int arglb(const std::vector<T> &v, const T &x) { return std::distance(v.begin(), std::lower_bound(v.begin(), v.end(), x)); }
template <typename T> int argub(const std::vector<T> &v, const T &x) { return std::distance(v.begin(), std::upper_bound(v.begin(), v.end(), x)); }
template <typename T> istream &operator>>(istream &is, vector<T> &vec) { for (auto &v : vec) is >> v; return is; }
template <typename T> ostream &operator<<(ostream &os, const vector<T> &vec) { os << '['; for (auto v : vec) os << v << ','; os << ']'; return os; }
template <typename T, size_t sz> ostream &operator<<(ostream &os, const array<T, sz> &arr) { os << '['; for (auto v : arr) os << v << ','; os << ']'; return os; }
#if __cplusplus >= 201703L
template <typename... T> istream &operator>>(istream &is, tuple<T...> &tpl) { std::apply([&is](auto &&... args) { ((is >> args), ...);}, tpl); return is; }
template <typename... T> ostream &operator<<(ostream &os, const tuple<T...> &tpl) { os << '('; std::apply([&os](auto &&... args) { ((os << args << ','), ...);}, tpl); return os << ')'; }
#endif
template <typename T> ostream &operator<<(ostream &os, const deque<T> &vec) { os << "deq["; for (auto v : vec) os << v << ','; os << ']'; return os; }
template <typename T> ostream &operator<<(ostream &os, const set<T> &vec) { os << '{'; for (auto v : vec) os << v << ','; os << '}'; return os; }
template <typename T, typename TH> ostream &operator<<(ostream &os, const unordered_set<T, TH> &vec) { os << '{'; for (auto v : vec) os << v << ','; os << '}'; return os; }
template <typename T> ostream &operator<<(ostream &os, const multiset<T> &vec) { os << '{'; for (auto v : vec) os << v << ','; os << '}'; return os; }
template <typename T> ostream &operator<<(ostream &os, const unordered_multiset<T> &vec) { os << '{'; for (auto v : vec) os << v << ','; os << '}'; return os; }
template <typename T1, typename T2> ostream &operator<<(ostream &os, const pair<T1, T2> &pa) { os << '(' << pa.first << ',' << pa.second << ')'; return os; }
template <typename TK, typename TV> ostream &operator<<(ostream &os, const map<TK, TV> &mp) { os << '{'; for (auto v : mp) os << v.first << "=>" << v.second << ','; os << '}'; return os; }
template <typename TK, typename TV, typename TH> ostream &operator<<(ostream &os, const unordered_map<TK, TV, TH> &mp) { os << '{'; for (auto v : mp) os << v.first << "=>" << v.second << ','; os << '}'; return os; }
#ifdef HITONANODE_LOCAL
const string COLOR_RESET = "\033[0m", BRIGHT_GREEN = "\033[1;32m", BRIGHT_RED = "\033[1;31m", BRIGHT_CYAN = "\033[1;36m", NORMAL_CROSSED = "\033[0;9;37m", RED_BACKGROUND = "\033[1;41m", NORMAL_FAINT = "\033[0;2m";
#define dbg(x) cerr << BRIGHT_CYAN << #x << COLOR_RESET << " = " << (x) << NORMAL_FAINT << " (L" << __LINE__ << ") " << __FILE__ << COLOR_RESET << endl
#define dbgif(cond, x) ((cond) ? cerr << BRIGHT_CYAN << #x << COLOR_RESET << " = " << (x) << NORMAL_FAINT << " (L" << __LINE__ << ") " << __FILE__ << COLOR_RESET << endl : cerr)
#else
#define dbg(x) (x)
#define dbgif(cond, x) 0
#endif

template <int md> struct ModInt {
#if __cplusplus >= 201402L
#define MDCONST constexpr
#else
#define MDCONST
#endif
    using lint = long long;
    MDCONST static int mod() { return md; }
    static int get_primitive_root() {
        static int primitive_root = 0;
        if (!primitive_root) {
            primitive_root = [&]() {
                std::set<int> fac;
                int v = md - 1;
                for (lint i = 2; i * i <= v; i++)
                    while (v % i == 0) fac.insert(i), v /= i;
                if (v > 1) fac.insert(v);
                for (int g = 1; g < md; g++) {
                    bool ok = true;
                    for (auto i : fac)
                        if (ModInt(g).pow((md - 1) / i) == 1) {
                            ok = false;
                            break;
                        }
                    if (ok) return g;
                }
                return -1;
            }();
        }
        return primitive_root;
    }
    int val;
    MDCONST ModInt() : val(0) {}
    MDCONST ModInt &_setval(lint v) { return val = (v >= md ? v - md : v), *this; }
    MDCONST ModInt(lint v) { _setval(v % md + md); }
    MDCONST explicit operator bool() const { return val != 0; }
    MDCONST ModInt operator+(const ModInt &x) const { return ModInt()._setval((lint)val + x.val); }
    MDCONST ModInt operator-(const ModInt &x) const {
        return ModInt()._setval((lint)val - x.val + md);
    }
    MDCONST ModInt operator*(const ModInt &x) const {
        return ModInt()._setval((lint)val * x.val % md);
    }
    MDCONST ModInt operator/(const ModInt &x) const {
        return ModInt()._setval((lint)val * x.inv() % md);
    }
    MDCONST ModInt operator-() const { return ModInt()._setval(md - val); }
    MDCONST ModInt &operator+=(const ModInt &x) { return *this = *this + x; }
    MDCONST ModInt &operator-=(const ModInt &x) { return *this = *this - x; }
    MDCONST ModInt &operator*=(const ModInt &x) { return *this = *this * x; }
    MDCONST ModInt &operator/=(const ModInt &x) { return *this = *this / x; }
    friend MDCONST ModInt operator+(lint a, const ModInt &x) {
        return ModInt()._setval(a % md + x.val);
    }
    friend MDCONST ModInt operator-(lint a, const ModInt &x) {
        return ModInt()._setval(a % md - x.val + md);
    }
    friend MDCONST ModInt operator*(lint a, const ModInt &x) {
        return ModInt()._setval(a % md * x.val % md);
    }
    friend MDCONST ModInt operator/(lint a, const ModInt &x) {
        return ModInt()._setval(a % md * x.inv() % md);
    }
    MDCONST bool operator==(const ModInt &x) const { return val == x.val; }
    MDCONST bool operator!=(const ModInt &x) const { return val != x.val; }
    MDCONST bool operator<(const ModInt &x) const {
        return val < x.val;
    } // To use std::map<ModInt, T>
    friend std::istream &operator>>(std::istream &is, ModInt &x) {
        lint t;
        return is >> t, x = ModInt(t), is;
    }
    MDCONST friend std::ostream &operator<<(std::ostream &os, const ModInt &x) {
        return os << x.val;
    }
    MDCONST ModInt pow(lint n) const {
        ModInt ans = 1, tmp = *this;
        while (n) {
            if (n & 1) ans *= tmp;
            tmp *= tmp, n >>= 1;
        }
        return ans;
    }

    static std::vector<ModInt> facs, facinvs, invs;
    MDCONST static void _precalculation(int N) {
        int l0 = facs.size();
        if (N > md) N = md;
        if (N <= l0) return;
        facs.resize(N), facinvs.resize(N), invs.resize(N);
        for (int i = l0; i < N; i++) facs[i] = facs[i - 1] * i;
        facinvs[N - 1] = facs.back().pow(md - 2);
        for (int i = N - 2; i >= l0; i--) facinvs[i] = facinvs[i + 1] * (i + 1);
        for (int i = N - 1; i >= l0; i--) invs[i] = facinvs[i] * facs[i - 1];
    }
    MDCONST lint inv() const {
        if (this->val < std::min(md >> 1, 1 << 21)) {
            while (this->val >= int(facs.size())) _precalculation(facs.size() * 2);
            return invs[this->val].val;
        } else {
            return this->pow(md - 2).val;
        }
    }
    MDCONST ModInt fac() const {
        while (this->val >= int(facs.size())) _precalculation(facs.size() * 2);
        return facs[this->val];
    }
    MDCONST ModInt facinv() const {
        while (this->val >= int(facs.size())) _precalculation(facs.size() * 2);
        return facinvs[this->val];
    }
    MDCONST ModInt doublefac() const {
        lint k = (this->val + 1) / 2;
        return (this->val & 1) ? ModInt(k * 2).fac() / (ModInt(2).pow(k) * ModInt(k).fac())
                               : ModInt(k).fac() * ModInt(2).pow(k);
    }
    MDCONST ModInt nCr(const ModInt &r) const {
        return (this->val < r.val) ? 0 : this->fac() * (*this - r).facinv() * r.facinv();
    }
    MDCONST ModInt nPr(const ModInt &r) const {
        return (this->val < r.val) ? 0 : this->fac() * (*this - r).facinv();
    }

    ModInt sqrt() const {
        if (val == 0) return 0;
        if (md == 2) return val;
        if (pow((md - 1) / 2) != 1) return 0;
        ModInt b = 1;
        while (b.pow((md - 1) / 2) == 1) b += 1;
        int e = 0, m = md - 1;
        while (m % 2 == 0) m >>= 1, e++;
        ModInt x = pow((m - 1) / 2), y = (*this) * x * x;
        x *= (*this);
        ModInt z = b.pow(m);
        while (y != 1) {
            int j = 0;
            ModInt t = y;
            while (t != 1) j++, t *= t;
            z = z.pow(1LL << (e - j - 1));
            x *= z, z *= z, y *= z;
            e = j;
        }
        return ModInt(std::min(x.val, md - x.val));
    }
};
template <int md> std::vector<ModInt<md>> ModInt<md>::facs = {1};
template <int md> std::vector<ModInt<md>> ModInt<md>::facinvs = {1};
template <int md> std::vector<ModInt<md>> ModInt<md>::invs = {0};
using mint = ModInt<998244353>;


#include <algorithm>
#include <cassert>
#include <cmath>
#include <iterator>
#include <type_traits>
#include <utility>
#include <vector>

namespace matrix_ {
struct has_id_method_impl {
    template <class T_> static auto check(T_ *) -> decltype(T_::id(), std::true_type());
    template <class T_> static auto check(...) -> std::false_type;
};
template <class T_> struct has_id : decltype(has_id_method_impl::check<T_>(nullptr)) {};
} // namespace matrix_

template <typename T> struct matrix {
    int H, W;
    std::vector<T> elem;
    typename std::vector<T>::iterator operator[](int i) { return elem.begin() + i * W; }
    inline T &at(int i, int j) { return elem[i * W + j]; }
    inline T get(int i, int j) const { return elem[i * W + j]; }
    int height() const { return H; }
    int width() const { return W; }
    std::vector<std::vector<T>> vecvec() const {
        std::vector<std::vector<T>> ret(H);
        for (int i = 0; i < H; i++) {
            std::copy(elem.begin() + i * W, elem.begin() + (i + 1) * W, std::back_inserter(ret[i]));
        }
        return ret;
    }
    operator std::vector<std::vector<T>>() const { return vecvec(); }
    matrix() = default;
    matrix(int H, int W) : H(H), W(W), elem(H * W) {}
    matrix(const std::vector<std::vector<T>> &d) : H(d.size()), W(d.size() ? d[0].size() : 0) {
        for (auto &raw : d) std::copy(raw.begin(), raw.end(), std::back_inserter(elem));
    }

    template <typename T2, typename std::enable_if<matrix_::has_id<T2>::value>::type * = nullptr>
    static T2 _T_id() {
        return T2::id();
    }
    template <typename T2, typename std::enable_if<!matrix_::has_id<T2>::value>::type * = nullptr>
    static T2 _T_id() {
        return T2(1);
    }

    static matrix Identity(int N) {
        matrix ret(N, N);
        for (int i = 0; i < N; i++) ret.at(i, i) = _T_id<T>();
        return ret;
    }

    matrix operator-() const {
        matrix ret(H, W);
        for (int i = 0; i < H * W; i++) ret.elem[i] = -elem[i];
        return ret;
    }
    matrix operator*(const T &v) const {
        matrix ret = *this;
        for (auto &x : ret.elem) x *= v;
        return ret;
    }
    matrix operator/(const T &v) const {
        matrix ret = *this;
        const T vinv = _T_id<T>() / v;
        for (auto &x : ret.elem) x *= vinv;
        return ret;
    }
    matrix operator+(const matrix &r) const {
        matrix ret = *this;
        for (int i = 0; i < H * W; i++) ret.elem[i] += r.elem[i];
        return ret;
    }
    matrix operator-(const matrix &r) const {
        matrix ret = *this;
        for (int i = 0; i < H * W; i++) ret.elem[i] -= r.elem[i];
        return ret;
    }
    matrix operator*(const matrix &r) const {
        matrix ret(H, r.W);
        for (int i = 0; i < H; i++) {
            for (int k = 0; k < W; k++) {
                for (int j = 0; j < r.W; j++) ret.at(i, j) += this->get(i, k) * r.get(k, j);
            }
        }
        return ret;
    }
    matrix &operator*=(const T &v) { return *this = *this * v; }
    matrix &operator/=(const T &v) { return *this = *this / v; }
    matrix &operator+=(const matrix &r) { return *this = *this + r; }
    matrix &operator-=(const matrix &r) { return *this = *this - r; }
    matrix &operator*=(const matrix &r) { return *this = *this * r; }
    bool operator==(const matrix &r) const { return H == r.H and W == r.W and elem == r.elem; }
    bool operator!=(const matrix &r) const { return H != r.H or W != r.W or elem != r.elem; }
    bool operator<(const matrix &r) const { return elem < r.elem; }
    matrix pow(int64_t n) const {
        matrix ret = Identity(H);
        bool ret_is_id = true;
        if (n == 0) return ret;
        for (int i = 63 - __builtin_clzll(n); i >= 0; i--) {
            if (!ret_is_id) ret *= ret;
            if ((n >> i) & 1) ret *= (*this), ret_is_id = false;
        }
        return ret;
    }
    std::vector<T> pow_vec(int64_t n, std::vector<T> vec) const {
        matrix x = *this;
        while (n) {
            if (n & 1) vec = x * vec;
            x *= x;
            n >>= 1;
        }
        return vec;
    };
    matrix transpose() const {
        matrix ret(W, H);
        for (int i = 0; i < H; i++) {
            for (int j = 0; j < W; j++) ret.at(j, i) = this->get(i, j);
        }
        return ret;
    }
    // Gauss-Jordan elimination
    // - Require inverse for every non-zero element
    // - Complexity: O(H^2 W)
    template <typename T2, typename std::enable_if<std::is_floating_point<T2>::value>::type * = nullptr>
    static int choose_pivot(const matrix<T2> &mtr, int h, int c) noexcept {
        int piv = -1;
        for (int j = h; j < mtr.H; j++) {
            if (mtr.get(j, c) and (piv < 0 or std::abs(mtr.get(j, c)) > std::abs(mtr.get(piv, c))))
                piv = j;
        }
        return piv;
    }
    template <typename T2, typename std::enable_if<!std::is_floating_point<T2>::value>::type * = nullptr>
    static int choose_pivot(const matrix<T2> &mtr, int h, int c) noexcept {
        for (int j = h; j < mtr.H; j++) {
            if (mtr.get(j, c) != T2()) return j;
        }
        return -1;
    }
    matrix gauss_jordan() const {
        int c = 0;
        matrix mtr(*this);
        std::vector<int> ws;
        ws.reserve(W);
        for (int h = 0; h < H; h++) {
            if (c == W) break;
            int piv = choose_pivot(mtr, h, c);
            if (piv == -1) {
                c++;
                h--;
                continue;
            }
            if (h != piv) {
                for (int w = 0; w < W; w++) {
                    std::swap(mtr[piv][w], mtr[h][w]);
                    mtr.at(piv, w) *= -_T_id<T>(); // To preserve sign of determinant
                }
            }
            ws.clear();
            for (int w = c; w < W; w++) {
                if (mtr.at(h, w) != T()) ws.emplace_back(w);
            }
            const T hcinv = _T_id<T>() / mtr.at(h, c);
            for (int hh = 0; hh < H; hh++)
                if (hh != h) {
                    const T coeff = mtr.at(hh, c) * hcinv;
                    for (auto w : ws) mtr.at(hh, w) -= mtr.at(h, w) * coeff;
                    mtr.at(hh, c) = T();
                }
            c++;
        }
        return mtr;
    }
    int rank_of_gauss_jordan() const {
        for (int i = H * W - 1; i >= 0; i--) {
            if (elem[i] != 0) return i / W + 1;
        }
        return 0;
    }
    T determinant_of_upper_triangle() const {
        T ret = _T_id<T>();
        for (int i = 0; i < H; i++) ret *= get(i, i);
        return ret;
    }
    int inverse() {
        assert(H == W);
        std::vector<std::vector<T>> ret = Identity(H), tmp = *this;
        int rank = 0;
        for (int i = 0; i < H; i++) {
            int ti = i;
            while (ti < H and tmp[ti][i] == 0) ti++;
            if (ti == H) {
                continue;
            } else {
                rank++;
            }
            ret[i].swap(ret[ti]), tmp[i].swap(tmp[ti]);
            T inv = _T_id<T>() / tmp[i][i];
            for (int j = 0; j < W; j++) ret[i][j] *= inv;
            for (int j = i + 1; j < W; j++) tmp[i][j] *= inv;
            for (int h = 0; h < H; h++) {
                if (i == h) continue;
                const T c = -tmp[h][i];
                for (int j = 0; j < W; j++) ret[h][j] += ret[i][j] * c;
                for (int j = i + 1; j < W; j++) tmp[h][j] += tmp[i][j] * c;
            }
        }
        *this = ret;
        return rank;
    }
    friend std::vector<T> operator*(const matrix &m, const std::vector<T> &v) {
        assert(m.W == int(v.size()));
        std::vector<T> ret(m.H);
        for (int i = 0; i < m.H; i++) {
            for (int j = 0; j < m.W; j++) ret[i] += m.get(i, j) * v[j];
        }
        return ret;
    }
    friend std::vector<T> operator*(const std::vector<T> &v, const matrix &m) {
        assert(int(v.size()) == m.H);
        std::vector<T> ret(m.W);
        for (int i = 0; i < m.H; i++) {
            for (int j = 0; j < m.W; j++) ret[j] += v[i] * m.get(i, j);
        }
        return ret;
    }
    std::vector<T> prod(const std::vector<T> &v) const { return (*this) * v; }
    std::vector<T> prod_left(const std::vector<T> &v) const { return v * (*this); }
    template <class OStream> friend OStream &operator<<(OStream &os, const matrix &x) {
        os << "[(" << x.H << " * " << x.W << " matrix)";
        os << "\n[column sums: ";
        for (int j = 0; j < x.W; j++) {
            T s = 0;
            for (int i = 0; i < x.H; i++) s += x.get(i, j);
            os << s << ",";
        }
        os << "]";
        for (int i = 0; i < x.H; i++) {
            os << "\n[";
            for (int j = 0; j < x.W; j++) os << x.get(i, j) << ",";
            os << "]";
        }
        os << "]\n";
        return os;
    }
    template <class IStream> friend IStream &operator>>(IStream &is, matrix &x) {
        for (auto &v : x.elem) is >> v;
        return is;
    }
};


// Solve Ax = b for T = ModInt<PRIME>
// - retval: {one of the solution, {freedoms}} (if solution exists)
//           {{}, {}} (otherwise)
// Complexity:
// - Yield one of the possible solutions: O(H^2 W) (H: # of eqs., W: # of variables)
// - Enumerate all of the bases: O(HW(H + W))
template <typename T>
std::pair<std::vector<T>, std::vector<std::vector<T>>>
system_of_linear_equations(matrix<T> A, std::vector<T> b) {
    int H = A.H, W = A.W;
    matrix<T> M(H, W + 1);
    for (int i = 0; i < H; i++) {
        for (int j = 0; j < W; j++) M[i][j] = A[i][j];
        M[i][W] = b[i];
    }
    M = M.gauss_jordan();
    std::vector<int> ss(W, -1);
    for (int i = 0; i < H; i++) {
        int j = 0;
        while (j <= W and M[i][j] == 0) j++;
        if (j == W) { // No solution
            return {{}, {}};
        }
        if (j < W) ss[j] = i;
    }
    std::vector<T> x(W);
    std::vector<std::vector<T>> D;
    for (int j = 0; j < W; j++) {
        if (ss[j] == -1) {
            std::vector<T> d(W);
            d[j] = 1;
            for (int jj = 0; jj < j; jj++) {
                if (ss[jj] != -1) d[jj] = -M[ss[jj]][j] / M[ss[jj]][jj];
            }
            D.emplace_back(d);
        } else
            x[j] = M[ss[j]][W] / M[ss[j]][j];
    }
    return std::make_pair(x, D);
}



int main() {
    int N;
    cin >> N;
    if (N == 2) {
        puts("2");
        return 0;
    }
    vector<vector<int>> n012s;
    REP(n0, N + 1) {
        REP(n1, N + 1 - n0) {
            int n2 = N - n0 - n1;
            n012s.push_back({n0, n1, n2});
        }
    }
    n012s = sort_unique(n012s);
    const int D = n012s.size();
    auto mat = matrix<mint>::Identity(D);
    vector<mint> vec(D, 1);
    vec.front() = 0;

    dbg(n012s);

    for (auto n012 : n012s) {
        if (n012 == vector<int>{0, 0, N}) continue;
        const int cur = arglb(n012s, n012);
        // mat[cur][cur] += 1;

        vector<ModInt<3>> init;
        REP(d, 3) {
            REP(_, n012[d]) init.push_back(d);
        }
        dbg(cur);
        dbg(init);
        mint den = mint(N * (N - 1) / 2).inv();
        REP(i, init.size()) {
            REP(j, i) {
                auto v = init;
                v[i] += 1;
                v[j] += 1;
                // dbg(v);
                vector<int> cntr(3);
                for (auto x : v) cntr[x.val]++;
                int nxt = arglb(n012s, cntr);
                // dbg(nxt);
                mat[cur][nxt] -= den;
            }
        }
    }
    dbg(mat);
    dbg(vec);
    auto sol = system_of_linear_equations(mat, vec);
    dbg(sol);
    cout << sol.first.back() << '\n';
}
accuracy: 1
time_ms: 20
memory_kb: 32368
final_rank: 20

code_snippet: #include<bits/stdc++.h>
using namespace std;
#define rep(i,n) for(ll i=0;i<n;i++)
#define repl(i,l,r) for(ll i=(l);i<(r);i++)
#define per(i,n) for(ll i=(n)-1;i>=0;i--)
#define perl(i,r,l) for(ll i=r-1;i>=l;i--)
#define fi first
#define se second
#define pb push_back
#define ins insert
#define pqueue(x) priority_queue<x,vector<x>,greater<x>>
#define all(x) (x).begin(),(x).end()
#define CST(x) cout<<fixed<<setprecision(x)
#define vtpl(x,y,z) vector<tuple<x,y,z>>
#define rev(x) reverse(x);
using ll=long long;
using vl=vector<ll>;
using vvl=vector<vector<ll>>;
using pl=pair<ll,ll>;
using vpl=vector<pl>;
using vvpl=vector<vpl>;
const ll MOD=1000000007;
const ll MOD9=998244353;
const int inf=1e9+10;
const ll INF=4e18;
const ll dy[9]={0,1,-1,0,1,1,-1,-1,0};
const ll dx[9]={1,0,0,-1,1,-1,1,-1,0};
template<class T> inline bool chmin(T& a, T b) {
    if (a > b) {
        a = b;
        return true;
    }
    return false;
}
template<class T> inline bool chmax(T& a, T b) {
    if (a < b) {
        a = b;
        return true;
    }
    return false;
}

const int mod = MOD9;
const int max_n = 200005;
struct mint {
  ll x; // typedef long long ll;
  mint(ll x=0):x((x%mod+mod)%mod){}
  mint operator-() const { return mint(-x);}
  mint& operator+=(const mint a) {
    if ((x += a.x) >= mod) x -= mod;
    return *this;
  }
  mint& operator-=(const mint a) {
    if ((x += mod-a.x) >= mod) x -= mod;
    return *this;
  }
  mint& operator*=(const mint a) { (x *= a.x) %= mod; return *this;}
  mint operator+(const mint a) const { return mint(*this) += a;}
  mint operator-(const mint a) const { return mint(*this) -= a;}
  mint operator*(const mint a) const { return mint(*this) *= a;}
  mint pow(ll t) const {
    if (!t) return 1;
    mint a = pow(t>>1);
    a *= a;
    if (t&1) a *= *this;
    return a;
  }
  bool operator==(const mint &p) const { return x == p.x; }
  bool operator!=(const mint &p) const { return x != p.x; }
  // for prime mod
  mint inv() const { return pow(mod-2);}
  mint& operator/=(const mint a) { return *this *= a.inv();}
  mint operator/(const mint a) const { return mint(*this) /= a;}
};
istream& operator>>(istream& is, mint& a) { return is >> a.x;}
ostream& operator<<(ostream& os, const mint& a) { return os << a.x;}
using vm=vector<mint>;
using vvm=vector<vm>;
struct combination {
  vector<mint> fact, ifact;
  combination(int n):fact(n+1),ifact(n+1) {
    assert(n < mod);
    fact[0] = 1;
    for (int i = 1; i <= n; ++i) fact[i] = fact[i-1]*i;
    ifact[n] = fact[n].inv();
    for (int i = n; i >= 1; --i) ifact[i-1] = ifact[i]*i;
  }
  mint operator()(int n, int k) {
    if (k < 0 || k > n) return 0;
    return fact[n]*ifact[k]*ifact[n-k];
  }
}comb(max_n);

vm gauss_jordan(const vvm &A,const vm &b){
    int n=A.size();
    vvm B(n,vm(n+1));
    rep(i,n){
        rep(j,n)B[i][j]=A[i][j];
    }
    rep(i,n)B[i][n]=b[i];
    rep(i,n){
        /*int pivot=i;
        for(int j=i;j<n;j++){
            if(B[j][i].x>B[pivot][i].x)pivot=j;
        }
        swap(B[i],B[pivot]);*/
        for(int j=i+1;j<=n;j++)B[i][j]/=B[i][i];
        rep(j,n){
            if(i!=j){
                for(int k=i+1;k<=n;k++)B[j][k]-=B[j][i]*B[i][k];
            }
        }
    }
    vm x(n);
    rep(i,n)x[i]=B[i][n];
    return x;
}
int main(){
    ll n;cin >> n;
    map<pl,ll> pls;
    ll cnt=0;
    rep(i,n+1){
        rep(j,n+1){
            rep(k,n+1){
                if(i+j+k==n){
                    pls[{i,j}]=cnt;cnt++;
                }
            }
        }
    }
    vvm mat(cnt,vm(cnt));
    mint inval=mint(1)/n/(n-1)*2;
    mint invtwo=mint(1)/2;
    rep(i,n+1){
        rep(j,n+1){
            rep(k,n+1){
                if(i+j+k!=n)continue;
                ll now=pls[{i,j}];
                mat[now][now]=-1;
                if(i==0&&j==0){
                    continue;
                }
                mint al=n*(n-1)/2;
                if(i>=2){
                    ll to=pls[{i-2,j+2}];
                    mat[now][to]=mint(i)*(i-1)*invtwo*inval;
                }
                if(i>=1&&j>=1){
                    ll to=pls[{i-1,j}];
                    mat[now][to]=mint(i)*(j)*inval;
                }
                if(i>=1&&k>=1){
                    ll to=pls[{i,j+1}];
                    mat[now][to]=mint(i)*(k)*inval;
                }
                if(j>=2){
                    ll to=pls[{i,j-2}];
                    mat[now][to]=mint(j)*(j-1)*invtwo*inval;
                }
                if(j>=1&&k>=1){
                    ll to=pls[{i+1,j-1}];
                    mat[now][to]=mint(j)*(k)*inval;
                }
                if(k>=2){
                    ll to=pls[{i+2,j}];
                    mat[now][to]=mint(k)*(k-1)*invtwo*inval;
                }
            }
        }
    }
    vm b(cnt,-1);b[pls[{0,0}]]=0;
    auto ans=gauss_jordan(mat,b);
   // for(auto p:ans)cout << p <<" ";cout << endl;
    cout << ans[pls[{n,0}]] << endl;
}
accuracy: 1
time_ms: 20
memory_kb: 7304
final_rank: 13

code_snippet: #include<bits/stdc++.h>
using namespace std;
#define rep(i,n) for(ll i=0;i<n;i++)
#define repl(i,l,r) for(ll i=(l);i<(r);i++)
#define per(i,n) for(ll i=(n)-1;i>=0;i--)
#define perl(i,r,l) for(ll i=r-1;i>=l;i--)
#define fi first
#define se second
#define pb push_back
#define ins insert
#define pqueue(x) priority_queue<x,vector<x>,greater<x>>
#define all(x) (x).begin(),(x).end()
#define CST(x) cout<<fixed<<setprecision(x)
#define vtpl(x,y,z) vector<tuple<x,y,z>>
#define rev(x) reverse(x);
using ll=long long;
using vl=vector<ll>;
using vvl=vector<vector<ll>>;
using pl=pair<ll,ll>;
using vpl=vector<pl>;
using vvpl=vector<vpl>;
const ll MOD=1000000007;
const ll MOD9=998244353;
const int inf=1e9+10;
const ll INF=4e18;
const ll dy[9]={0,1,-1,0,1,1,-1,-1,0};
const ll dx[9]={1,0,0,-1,1,-1,1,-1,0};
template<class T> inline bool chmin(T& a, T b) {
    if (a > b) {
        a = b;
        return true;
    }
    return false;
}
template<class T> inline bool chmax(T& a, T b) {
    if (a < b) {
        a = b;
        return true;
    }
    return false;
}

const int mod = MOD9;
const int max_n = 200005;
struct mint {
  ll x; // typedef long long ll;
  mint(ll x=0):x((x%mod+mod)%mod){}
  mint operator-() const { return mint(-x);}
  mint& operator+=(const mint a) {
    if ((x += a.x) >= mod) x -= mod;
    return *this;
  }
  mint& operator-=(const mint a) {
    if ((x += mod-a.x) >= mod) x -= mod;
    return *this;
  }
  mint& operator*=(const mint a) { (x *= a.x) %= mod; return *this;}
  mint operator+(const mint a) const { return mint(*this) += a;}
  mint operator-(const mint a) const { return mint(*this) -= a;}
  mint operator*(const mint a) const { return mint(*this) *= a;}
  mint pow(ll t) const {
    if (!t) return 1;
    mint a = pow(t>>1);
    a *= a;
    if (t&1) a *= *this;
    return a;
  }
  bool operator==(const mint &p) const { return x == p.x; }
  bool operator!=(const mint &p) const { return x != p.x; }
  // for prime mod
  mint inv() const { return pow(mod-2);}
  mint& operator/=(const mint a) { return *this *= a.inv();}
  mint operator/(const mint a) const { return mint(*this) /= a;}
};
istream& operator>>(istream& is, mint& a) { return is >> a.x;}
ostream& operator<<(ostream& os, const mint& a) { return os << a.x;}
using vm=vector<mint>;
using vvm=vector<vm>;
struct combination {
  vector<mint> fact, ifact;
  combination(int n):fact(n+1),ifact(n+1) {
    assert(n < mod);
    fact[0] = 1;
    for (int i = 1; i <= n; ++i) fact[i] = fact[i-1]*i;
    ifact[n] = fact[n].inv();
    for (int i = n; i >= 1; --i) ifact[i-1] = ifact[i]*i;
  }
  mint operator()(int n, int k) {
    if (k < 0 || k > n) return 0;
    return fact[n]*ifact[k]*ifact[n-k];
  }
}comb(max_n);

vm gauss_jordan(const vvm &A,const vm &b){
    int n=A.size();
    vvm B(n,vm(n+1));
    rep(i,n){
        rep(j,n)B[i][j]=A[i][j];
    }
    rep(i,n)B[i][n]=b[i];
    rep(i,n){
        int pivot=i;
        for(int j=i;j<n;j++){
            if(B[j][i].x>B[pivot][i].x)pivot=j;
        }
        swap(B[i],B[pivot]);
        for(int j=i+1;j<=n;j++)B[i][j]/=B[i][i];
        rep(j,n){
            if(i!=j){
                for(int k=i+1;k<=n;k++)B[j][k]-=B[j][i]*B[i][k];
            }
        }
    }
    vm x(n);
    rep(i,n)x[i]=B[i][n];
    return x;
}
int main(){
    ll n;cin >> n;
    map<pl,ll> pls;
    ll cnt=0;
    rep(i,n+1){
        rep(j,n+1){
            rep(k,n+1){
                if(i+j+k==n){
                    pls[{i,j}]=cnt;cnt++;
                }
            }
        }
    }
    vvm mat(cnt,vm(cnt));
    mint inval=mint(1)/n/(n-1)*2;
    mint invtwo=mint(1)/2;
    rep(i,n+1){
        rep(j,n+1){
            rep(k,n+1){
                if(i+j+k!=n)continue;
                ll now=pls[{i,j}];
                mat[now][now]=-1;
                if(i==0&&j==0){
                    continue;
                }
                mint al=n*(n-1)/2;
                if(i>=2){
                    ll to=pls[{i-2,j+2}];
                    mat[now][to]=mint(i)*(i-1)*invtwo*inval;
                }
                if(i>=1&&j>=1){
                    ll to=pls[{i-1,j}];
                    mat[now][to]=mint(i)*(j)*inval;
                }
                if(i>=1&&k>=1){
                    ll to=pls[{i,j+1}];
                    mat[now][to]=mint(i)*(k)*inval;
                }
                if(j>=2){
                    ll to=pls[{i,j-2}];
                    mat[now][to]=mint(j)*(j-1)*invtwo*inval;
                }
                if(j>=1&&k>=1){
                    ll to=pls[{i+1,j-1}];
                    mat[now][to]=mint(j)*(k)*inval;
                }
                if(k>=2){
                    ll to=pls[{i+2,j}];
                    mat[now][to]=mint(k)*(k-1)*invtwo*inval;
                }
            }
        }
    }
    vm b(cnt,-1);b[pls[{0,0}]]=0;
    auto ans=gauss_jordan(mat,b);
   // for(auto p:ans)cout << p <<" ";cout << endl;
    cout << ans[pls[{n,0}]] << endl;

}
accuracy: 1
time_ms: 10
memory_kb: 7336
final_rank: 11

code_snippet: #pragma GCC optimize("Ofast")
#include <iostream>
#include <vector>
#include <algorithm>
#include <map>
#include <queue>
#include <cstdio>
#include <ctime>
#include <assert.h>
#include <chrono>
#include <random>
#include <numeric>
#include <set>
#include <deque>
#include <stack>
#include <sstream>
#include <utility>
#include <cstring>
#include <unordered_map>
#include <unordered_set>
#include <tuple>
#include <array>
#include <bitset>
using namespace std;
typedef long long int ll;
typedef unsigned long long ull;

mt19937_64 rng(chrono::steady_clock::now().time_since_epoch().count());
ll myRand(ll B) {
    return (ull)rng() % B;
}
inline ll time() {
    return static_cast<long double>(chrono::duration_cast<chrono::nanoseconds>(chrono::steady_clock::now().time_since_epoch()).count()) * 1e-9;
}

constexpr ll mod = 998244353;

ll mod_pow(ll a,ll b){
    a%=mod;
    if(b==0)return 1;
    if(b==1)return a;
    ll res=mod_pow(a,b/2)%mod;
    res*=res; res%=mod;
    if(b%2)res*=a;
    return res%mod;
}

struct perm{
private:
    int sz;
    vector<ll> p,invp;
public:
    perm(int n){
        sz=n+1;
        p.resize(sz),invp.resize(sz);
        p[0]=1;
        for(int i=1;i<=sz-1;i++){
            p[i]=p[i-1]*i%mod;
        }
        invp[sz-1]=mod_pow(p[sz-1],mod-2);
        for(int i=sz-2;i>=0;i--){
            invp[i]=invp[i+1]*(i+1)%mod;
        }
    }
    ll comb(ll x,ll y){
        if(x<y||y<0)return 0;
        return (p[x]*invp[x-y]%mod)*invp[y]%mod;
    }
};
perm P(1<<20);


template<typename T> using V = vector<T>;
template<typename T> using VV = vector<vector<T>>;
template<typename T> using PQ = priority_queue<T>;
template<typename T> using PQR = priority_queue<T,vector<T>,greater<T>>;
#define fs first
#define sc second
#define pb push_back
#define mp make_pair
#define mt make_tuple
#define eb emplace_back
#define lb(c, x) distance((c).begin(), lower_bound(all(c), (x)))
#define ub(c, x) distance((c).begin(), upper_bound(all(c), (x)))
#define all(v) (v).begin(),(v).end()
#define siz(v) (ll)(v).size()
#define rep(i,a,n) for(ll i=a;i<(ll)(n);++i)
#define repr(i,a,n) for(ll i=n-1;(ll)a<=i;--i)
#define pw2(n) (1ll<<(n))
#define pcntl(x) __builtin_popcountll(x)
#define ENDL '\n'
typedef pair<int,int> Pi;
typedef pair<ll,ll> PL;

template <const long long MOD = 998244353> // MODを適宜設定すること
struct Mint {
  using M = Mint;
  ll a;
  Mint(ll x = 0) {
    a = x % MOD;
    if (a < 0)
      a += MOD;
  } // Mintで

  M pow(ll n) {
    assert(n >= 0);
    M b = *this, r = 1 % MOD;
    while (n) {
      if (n & 1)
        r *= b;
      b *= b;
      n >>= 1;
    }
    return r;
  }

  M inv() { return pow(MOD - 2); }

  M &operator+=(M r) {
    a += r.a;
    if (a >= MOD)
      a -= MOD;
    return *this;
  }
  M &operator-=(M r) {
    a += MOD - r.a;
    if (a >= MOD)
      a -= MOD;
    return *this;
  }
  M &operator*=(M r) {
    a = 1LL * a * r.a % MOD;
    return *this;
  }
  M &operator/=(M r) { return (*this) *= r.inv(); }
  M operator+(M r) const { return M(a) += r; };
  M operator-(M r) const { return M(a) -= r; };
  M operator*(M r) const { return M(a) *= r; };
  M operator/(M r) const { return M(a) /= r; };
  M operator-() const { return M() - *this; }

  friend ostream &operator<<(ostream &os, const M &r) { return os << r.a; }
};
using M = Mint<998244353>;

// is_extendedがtrueのとき、aはAx=BのAとBを連結した行列
int GaussJordan(VV<M> &a, bool is_extended = false) {
  int m = a.size(), n = a[0].size(), rank = 0;
  assert(m);

  rep(c, 0, n) {
    if (is_extended && c == n - 1)
      break;
    int pivot = -1;
    rep(r, rank, m) {
      if (a[r][c].a != 0) {
        pivot = r;
        break;
      }
    }

    if (pivot == -1)
      continue;

    swap(a[pivot], a[rank]);
    M inv = a[rank][c].inv();
    rep(c2, 0, n) a[rank][c2] *= inv;

    rep(r, 0, m) {
      if (r != rank && a[r][c].a) {
        M fac = a[r][c];
        rep(c2, 0, n) a[r][c2] -= a[rank][c2] * fac;
      }
    }
    ++rank;
  }
  return rank;
}

int linear_equation(VV<M> a, vector<ll> b, vector<ll> &res) {
  int m = a.size(), n = a[0].size();
  VV<M> mat(m, V<M>(n + 1));
  rep(i, 0, m) {
    rep(j, 0, n) mat[i][j] = a[i][j];
    mat[i][n] = b[i];
  }
  int rank = GaussJordan(mat, true);

  rep(r, rank, m) if (mat[r][n].a) return -1;

  // answer
  res.assign(n, 0);
  rep(i, 0, rank) res[i] = mat[i][n].a;
  return rank;
}
/*
-1 : 解なし
rank=nのとき、resに解が入る
rank<nのとき、(n-rank)個の値を決めると解が定まる
*/

using state = array<int,3>;

int main(){
    cin.tie(nullptr);
    ios::sync_with_stdio(false);
    int n; cin >> n;
    vector<state> v;
    map<state,int> mp;
    auto find_id=[&](state x)->bool{
        if(mp.find(x) == mp.end()){
            int sz = mp.size();
            v.push_back(x);
            mp[x] = sz; return true;
        }
        return false;
    };
    vector<pair<pair<int,int>,ll>> edge;
    // 遷移列挙
    {
        queue<int> q;
        state init = {n,0,0};
        mp[init] = 0;
        v.push_back(init);
        q.push(0);
        const ll inv = mod_pow(P.comb(n,2), mod-2);
        auto add1=[&](int x)->int{
            if(x+1 == 3)return 0;
            else return x+1;
        };
        while(q.size()){
            int _ = q.front(); q.pop();
            auto cur = v[_];
            for(int i=0;i<3;i++){
                for(int j=i;j<3;j++){
                    if(i == j and cur[i]<2)continue;
                    if(cur[i]<1 or cur[j]<1)continue;
                    int ni = add1(i);
                    int nj = add1(j);
                    cur[i]--; cur[j]--;
                    cur[ni]++; cur[nj]++;
                    if(find_id(cur)){
                        q.push(mp[cur]);
                    }
                    int nxt = mp[cur];
                    cur[i]++; cur[j]++;
                    cur[ni]--; cur[nj]--;
                    ll r;
                    if(i == j){
                        r = P.comb(cur[i],2);
                    }
                    else{
                        r = (ll)cur[i]*(ll)cur[j];
                    }
                    r *= inv;
                    r %= mod;
                    edge.push_back({{_,nxt},r});
                }
            }
        }
    }
    int goal = mp[{0,0,n}];
    int sz = mp.size();
    vector<vector<M>> A(sz,vector<M>(sz));
    vector<ll> B(sz);
    for(auto e:edge){
        if(e.first.first == goal)continue;
        // cout << e.first.first << " " << e.first.second << " " << e.second << endl;
        A[e.first.first][e.first.second] = -e.second;
    }
    for(int i=0;i<sz;i++){
        A[i][i] = 1;
        B[i] = (goal != i);
    }
    vector<ll> res;
    auto r = linear_equation(A, B, res);
    // ll sum = 0;
    // for(int i=0;i<sz;i++){
    //     cout << res[i] << endl;
    //     sum += res[i];
    // }
    cout << res[0] << endl;
}
accuracy: 1
time_ms: 30
memory_kb: 20992
final_rank: 16

code_snippet: #include <bits/stdc++.h>
#pragma region Header
using i32 = int;
using u32 = unsigned int;
using i64 = long long;
using u64 = unsigned long long;
using i128 = __int128_t;
using u128 = __uint128_t;
using f64 = double;
using f80 = long double;
using f128 = __float128;
constexpr i32 operator"" _i32(u64 v)
{
    return v;
}
constexpr i32 operator"" _u32(u64 v)
{
    return v;
}
constexpr i64 operator"" _i64(u64 v)
{
    return v;
}
constexpr u64 operator"" _u64(u64 v)
{
    return v;
}
constexpr f64 operator"" _f64(f80 v)
{
    return v;
}
constexpr f80 operator"" _f80(f80 v)
{
    return v;
}
using Istream = std::istream;
using Ostream = std::ostream;
using Str = std::string;
template<typename T>
using Lt = std::less<T>;
template<typename T>
using Gt = std::greater<T>;
template<typename T>
using IList = std::initializer_list<T>;
template<int n>
using BSet = std::bitset<n>;
template<typename T1, typename T2>
using Pair = std::pair<T1, T2>;
template<typename... Ts>
using Tup = std::tuple<Ts...>;
template<typename T, int N>
using Arr = std::array<T, N>;
template<typename... Ts>
using Deq = std::deque<Ts...>;
template<typename... Ts>
using Set = std::set<Ts...>;
template<typename... Ts>
using MSet = std::multiset<Ts...>;
template<typename... Ts>
using USet = std::unordered_set<Ts...>;
template<typename... Ts>
using UMSet = std::unordered_multiset<Ts...>;
template<typename... Ts>
using Map = std::map<Ts...>;
template<typename... Ts>
using MMap = std::multimap<Ts...>;
template<typename... Ts>
using UMap = std::unordered_map<Ts...>;
template<typename... Ts>
using UMMap = std::unordered_multimap<Ts...>;
template<typename... Ts>
using Vec = std::vector<Ts...>;
template<typename... Ts>
using Stack = std::stack<Ts...>;
template<typename... Ts>
using Queue = std::queue<Ts...>;
template<typename T>
using MaxHeap = std::priority_queue<T>;
template<typename T>
using MinHeap = std::priority_queue<T, Vec<T>, Gt<T>>;
using NSec = std::chrono::nanoseconds;
using USec = std::chrono::microseconds;
using MSec = std::chrono::milliseconds;
using Sec = std::chrono::seconds;
template<typename T>
constexpr T LIMMIN = std::numeric_limits<T>::min();
template<typename T>
constexpr T LIMMAX = std::numeric_limits<T>::max();
template<typename T>
constexpr T INF = (LIMMAX<T> - 1) / 2;
template<typename T>
constexpr T PI = T{3.141592653589793238462643383279502884};
template<typename T = u64>
constexpr T TEN(const int n)
{
    return n == 0 ? T{1} : TEN<T>(n - 1) * T{10};
}
Ostream& operator<<(Ostream& os, i128 v)
{
    bool minus = false;
    if (v < 0) { minus = true, v = -v; }
    Str ans;
    if (v == 0) { ans = "0"; }
    while (v) {
        ans.push_back('0' + v % 10), v /= 10;
    }
    std::reverse(ans.begin(), ans.end());
    return os << (minus ? "-" : "") << ans;
}
Ostream& operator<<(Ostream& os, u128 v)
{
    Str ans;
    if (v == 0) { ans = "0"; }
    while (v) {
        ans.push_back('0' + v % 10), v /= 10;
    }
    std::reverse(ans.begin(), ans.end());
    return os << ans;
}
template<typename T>
bool chmin(T& a, const T& b)
{
    if (a > b) {
        a = b;
        return true;
    } else {
        return false;
    }
}
template<typename T>
bool chmax(T& a, const T& b)
{
    if (a < b) {
        a = b;
        return true;
    } else {
        return false;
    }
}
template<typename T>
constexpr T floorDiv(T x, T y)
{
    if (y < T{}) { x = -x, y = -y; }
    return x >= T{} ? x / y : (x - y + 1) / y;
}
template<typename T>
constexpr T ceilDiv(T x, T y)
{
    if (y < T{}) { x = -x, y = -y; }
    return x >= T{} ? (x + y - 1) / y : x / y;
}
template<typename T, typename I>
constexpr T modPower(T v, I n, T mod)
{
    T ans = 1 % mod;
    for (; n > 0; n >>= 1, (v *= v) %= mod) {
        if (n % 2 == 1) { (ans *= v) %= mod; }
    }
    return ans;
}
template<typename T, typename I>
constexpr T power(T v, I n)
{
    T ans = 1;
    for (; n > 0; n >>= 1, v *= v) {
        if (n % 2 == 1) { ans *= v; }
    }
    return ans;
}
template<typename T, typename I>
constexpr T power(T v, I n, const T& e)
{
    T ans = e;
    for (; n > 0; n >>= 1, v *= v) {
        if (n % 2 == 1) { ans *= v; }
    }
    return ans;
}
template<typename T>
Vec<T> operator+=(Vec<T>& vs1, const Vec<T>& vs2)
{
    vs1.insert(vs1.end(), vs2.begin(), vs2.end());
    return vs1;
}
template<typename T>
Vec<T> operator+(const Vec<T>& vs1, const Vec<T>& vs2)
{
    auto vs = vs1;
    vs += vs2;
    return vs;
}
template<typename Vs, typename V>
void fillAll(Vs& arr, const V& v)
{
    if constexpr (std::is_convertible<V, Vs>::value) {
        arr = v;
    } else {
        for (auto& subarr : arr) {
            fillAll(subarr, v);
        }
    }
}
template<typename Vs>
void sortAll(Vs& vs)
{
    std::sort(std::begin(vs), std::end(vs));
}
template<typename Vs, typename C>
void sortAll(Vs& vs, C comp)
{
    std::sort(std::begin(vs), std::end(vs), comp);
}
template<typename Vs>
void reverseAll(Vs& vs)
{
    std::reverse(std::begin(vs), std::end(vs));
}
template<typename V, typename Vs>
V sumAll(const Vs& vs)
{
    if constexpr (std::is_convertible<Vs, V>::value) {
        return static_cast<V>(vs);
    } else {
        V ans = 0;
        for (const auto& v : vs) {
            ans += sumAll<V>(v);
        }
        return ans;
    }
}
template<typename Vs>
int minInd(const Vs& vs)
{
    return std::min_element(std::begin(vs), std::end(vs)) - std::begin(vs);
}
template<typename Vs>
int maxInd(const Vs& vs)
{
    return std::max_element(std::begin(vs), std::end(vs)) - std::begin(vs);
}
template<typename Vs, typename V>
int lbInd(const Vs& vs, const V& v)
{
    return std::lower_bound(std::begin(vs), std::end(vs), v) - std::begin(vs);
}
template<typename Vs, typename V>
int ubInd(const Vs& vs, const V& v)
{
    return std::upper_bound(std::begin(vs), std::end(vs), v) - std::begin(vs);
}
template<typename T, typename F>
Vec<T> genVec(int n, F gen)
{
    Vec<T> ans;
    std::generate_n(std::back_insert_iterator(ans), n, gen);
    return ans;
}
Vec<int> iotaVec(int n, int offset = 0)
{
    Vec<int> ans(n);
    std::iota(ans.begin(), ans.end(), offset);
    return ans;
}
constexpr int popcount(const u64 v)
{
    return v ? __builtin_popcountll(v) : 0;
}
constexpr int log2p1(const u64 v)
{
    return v ? 64 - __builtin_clzll(v) : 0;
}
constexpr int lsbp1(const u64 v)
{
    return __builtin_ffsll(v);
}
constexpr int clog(const u64 v)
{
    return v ? log2p1(v - 1) : 0;
}
constexpr u64 ceil2(const u64 v)
{
    const int l = clog(v);
    return (l == 64) ? 0_u64 : (1_u64 << l);
}
constexpr u64 floor2(const u64 v)
{
    return v ? (1_u64 << (log2p1(v) - 1)) : 0_u64;
}
constexpr bool ispow2(const u64 v)
{
    return (v > 0) and ((v & (v - 1)) == 0);
}
constexpr bool btest(const u64 mask, const int ind)
{
    return (mask >> ind) & 1_u64;
}
template<typename F>
struct Fix : F
{
    Fix(F&& f) : F{std::forward<F>(f)} {}
    template<typename... Args>
    auto operator()(Args&&... args) const
    {
        return F::operator()(*this, std::forward<Args>(args)...);
    }
};
class irange
{
private:
    struct itr
    {
        itr(i64 start = 0, i64 step = 1) : m_cnt{start}, m_step{step} {}
        bool operator!=(const itr& it) const
        {
            return m_cnt != it.m_cnt;
        }
        int operator*()
        {
            return m_cnt;
        }
        itr& operator++()
        {
            m_cnt += m_step;
            return *this;
        }
        i64 m_cnt, m_step;
    };
    i64 m_start, m_end, m_step;
public:
    irange(i64 start, i64 end, i64 step = 1)
    {
        assert(step != 0);
        const i64 d = std::abs(step);
        const i64 l = (step > 0 ? start : end);
        const i64 r = (step > 0 ? end : start);
        int n = (r - l) / d + ((r - l) % d ? 1 : 0);
        if (l >= r) { n = 0; }
        m_start = start;
        m_end = start + step * n;
        m_step = step;
    }
    itr begin() const
    {
        return itr{m_start, m_step};
    }
    itr end() const
    {
        return itr{m_end, m_step};
    }
};
irange rep(int end)
{
    return irange(0, end, 1);
}
irange per(int rend)
{
    return irange(rend - 1, -1, -1);
}
#pragma COMMENT("[REFS] Xoshiro: https://prng.di.unimi.it")
namespace xoshiro_impl {
u64 x;
u64 next()
{
    uint64_t z = (x += 0x9e3779b97f4a7c15);
    z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9;
    z = (z ^ (z >> 27)) * 0x94d049bb133111eb;
    return z ^ (z >> 31);
}
} // namespace xoshiro_impl
class Xoshiro32
{
public:
    using result_type = u32;
    using T = result_type;
    Xoshiro32(T seed = 0)
    {
        xoshiro_impl::x = seed;
        s[0] = xoshiro_impl::next();
        s[1] = xoshiro_impl::next();
        s[2] = xoshiro_impl::next();
        s[3] = xoshiro_impl::next();
    }
    static constexpr T min()
    {
        return LIMMIN<T>;
    }
    static constexpr T max()
    {
        return LIMMAX<T>;
    }
    T operator()()
    {
        return next();
    }
private:
    static constexpr T rotl(const T x, int k)
    {
        return (x << k) | (x >> (32 - k));
    }
    T next()
    {
        const T ans = rotl(s[1] * 5, 7) * 9;
        const T t = s[1] << 9;
        s[2] ^= s[0];
        s[3] ^= s[1];
        s[1] ^= s[2];
        s[0] ^= s[3];
        s[2] ^= t;
        s[3] = rotl(s[3], 11);
        return ans;
    }
    T s[4];
};
class Xoshiro64
{
public:
    using result_type = u64;
    using T = result_type;
    Xoshiro64(T seed = 0)
    {
        xoshiro_impl::x = seed;
        s[0] = xoshiro_impl::next();
        s[1] = xoshiro_impl::next();
        s[2] = xoshiro_impl::next();
        s[3] = xoshiro_impl::next();
    }
    static constexpr T min()
    {
        return LIMMIN<T>;
    }
    static constexpr T max()
    {
        return LIMMAX<T>;
    }
    T operator()()
    {
        return next();
    }
private:
    static constexpr T rotl(const T x, int k)
    {
        return (x << k) | (x >> (64 - k));
    }
    T next()
    {
        const T ans = rotl(s[1] * 5, 7) * 9;
        const T t = s[1] << 17;
        s[2] ^= s[0];
        s[3] ^= s[1];
        s[1] ^= s[2];
        s[0] ^= s[3];
        s[2] ^= t;
        s[3] = rotl(s[3], 45);
        return ans;
    }
    T s[4];
};
template<typename Rng>
class RNG
{
public:
    using result_type = typename Rng::result_type;
    using T = result_type;
    static constexpr T min()
    {
        return Rng::min();
    }
    static constexpr T max()
    {
        return Rng::max();
    }
    RNG() : RNG(std::random_device{}()) {}
    RNG(T seed) : m_rng(seed) {}
    T operator()()
    {
        return m_rng();
    }
    template<typename T>
    T val(T min, T max)
    {
        return std::uniform_int_distribution<T>(min, max)(m_rng);
    }
    template<typename T>
    Pair<T, T> pair(T min, T max)
    {
        return std::minmax({val<T>(min, max), val<T>(min, max)});
    }
    template<typename T>
    Vec<T> vec(int n, T min, T max)
    {
        return genVec<T>(n, [&]() { return val<T>(min, max); });
    }
    template<typename T>
    Vec<Vec<T>> vvec(int n, int m, T min, T max)
    {
        return genVec<Vec<T>>(n, [&]() { return vec(m, min, max); });
    }
private:
    Rng m_rng;
};
RNG<std::mt19937> rng;
RNG<std::mt19937_64> rng64;
RNG<Xoshiro32> rng_xo;
RNG<Xoshiro64> rng_xo64;
class Scanner
{
public:
    Scanner(Istream& is = std::cin) : m_is{is}
    {
        m_is.tie(nullptr)->sync_with_stdio(false);
    }
    template<typename T>
    T val()
    {
        T v;
        return m_is >> v, v;
    }
    template<typename T>
    T val(T offset)
    {
        return val<T>() - offset;
    }
    template<typename T>
    Vec<T> vec(int n)
    {
        return genVec<T>(n, [&]() { return val<T>(); });
    }
    template<typename T>
    Vec<T> vec(int n, T offset)
    {
        return genVec<T>(n, [&]() { return val<T>(offset); });
    }
    template<typename T>
    Vec<Vec<T>> vvec(int n, int m)
    {
        return genVec<Vec<T>>(n, [&]() { return vec<T>(m); });
    }
    template<typename T>
    Vec<Vec<T>> vvec(int n, int m, const T offset)
    {
        return genVec<Vec<T>>(n, [&]() { return vec<T>(m, offset); });
    }
    template<typename... Args>
    auto tup()
    {
        return Tup<Args...>{val<Args>()...};
    }
    template<typename... Args>
    auto tup(const Args&... offsets)
    {
        return Tup<Args...>{val<Args>(offsets)...};
    }
private:
    Istream& m_is;
};
Scanner in;
class Printer
{
public:
    Printer(Ostream& os = std::cout) : m_os{os}
    {
        m_os << std::fixed << std::setprecision(15);
    }
    template<typename... Args>
    int operator()(const Args&... args)
    {
        dump(args...);
        return 0;
    }
    template<typename... Args>
    int ln(const Args&... args)
    {
        dump(args...), m_os << '\n';
        return 0;
    }
    template<typename... Args>
    int el(const Args&... args)
    {
        dump(args...), m_os << std::endl;
        return 0;
    }
private:
    template<typename T>
    void dump(const T& v)
    {
        m_os << v;
    }
    template<typename T>
    void dump(const Vec<T>& vs)
    {
        for (const int i : rep(vs.size())) {
            m_os << (i ? " " : ""), dump(vs[i]);
        }
    }
    template<typename T>
    void dump(const Vec<Vec<T>>& vss)
    {
        for (const int i : rep(vss.size())) {
            m_os << (i ? "\n" : ""), dump(vss[i]);
        }
    }
    template<typename T, typename... Ts>
    int dump(const T& v, const Ts&... args)
    {
        dump(v), m_os << ' ', dump(args...);
        return 0;
    }
    Ostream& m_os;
};
Printer out;
template<typename T, int n, int i = 0>
auto ndVec(int const (&szs)[n], const T x = T{})
{
    if constexpr (i == n) {
        return x;
    } else {
        return std::vector(szs[i], ndVec<T, n, i + 1>(szs, x));
    }
}
template<typename T, typename F>
T binSearch(T ng, T ok, F check)
{
    while (std::abs(ok - ng) > 1) {
        const T mid = (ok + ng) / 2;
        (check(mid) ? ok : ng) = mid;
    }
    return ok;
}
template<u32 mod_, u32 root_, u32 max2p_>
class modint
{
    template<typename U = u32&>
    static U modRef()
    {
        static u32 s_mod = 0;
        return s_mod;
    }
    template<typename U = u32&>
    static U rootRef()
    {
        static u32 s_root = 0;
        return s_root;
    }
    template<typename U = u32&>
    static U max2pRef()
    {
        static u32 s_max2p = 0;
        return s_max2p;
    }
public:
    template<typename U = const u32>
    static constexpr std::enable_if_t<mod_ != 0, U> mod()
    {
        return mod_;
    }
    template<typename U = const u32>
    static std::enable_if_t<mod_ == 0, U> mod()
    {
        return modRef();
    }
    template<typename U = const u32>
    static constexpr std::enable_if_t<mod_ != 0, U> root()
    {
        return root_;
    }
    template<typename U = const u32>
    static std::enable_if_t<mod_ == 0, U> root()
    {
        return rootRef();
    }
    template<typename U = const u32>
    static constexpr std::enable_if_t<mod_ != 0, U> max2p()
    {
        return max2p_;
    }
    template<typename U = const u32>
    static std::enable_if_t<mod_ == 0, U> max2p()
    {
        return max2pRef();
    }
    template<typename U = u32>
    static void setMod(std::enable_if_t<mod_ == 0, U> m)
    {
        modRef() = m;
    }
    template<typename U = u32>
    static void setRoot(std::enable_if_t<mod_ == 0, U> r)
    {
        rootRef() = r;
    }
    template<typename U = u32>
    static void setMax2p(std::enable_if_t<mod_ == 0, U> m)
    {
        max2pRef() = m;
    }
    constexpr modint() : m_val{0} {}
    constexpr modint(i64 v) : m_val{normll(v)} {}
    constexpr void setRaw(u32 v)
    {
        m_val = v;
    }
    constexpr modint operator-() const
    {
        return modint{0} - (*this);
    }
    constexpr modint& operator+=(const modint& m)
    {
        m_val = norm(m_val + m.val());
        return *this;
    }
    constexpr modint& operator-=(const modint& m)
    {
        m_val = norm(m_val + mod() - m.val());
        return *this;
    }
    constexpr modint& operator*=(const modint& m)
    {
        m_val = normll((i64)m_val * (i64)m.val() % (i64)mod());
        return *this;
    }
    constexpr modint& operator/=(const modint& m)
    {
        return *this *= m.inv();
    }
    constexpr modint operator+(const modint& m) const
    {
        auto v = *this;
        return v += m;
    }
    constexpr modint operator-(const modint& m) const
    {
        auto v = *this;
        return v -= m;
    }
    constexpr modint operator*(const modint& m) const
    {
        auto v = *this;
        return v *= m;
    }
    constexpr modint operator/(const modint& m) const
    {
        auto v = *this;
        return v /= m;
    }
    constexpr bool operator==(const modint& m) const
    {
        return m_val == m.val();
    }
    constexpr bool operator!=(const modint& m) const
    {
        return not(*this == m);
    }
    friend Istream& operator>>(Istream& is, modint& m)
    {
        i64 v;
        return is >> v, m = v, is;
    }
    friend Ostream& operator<<(Ostream& os, const modint& m)
    {
        return os << m.val();
    }
    constexpr u32 val() const
    {
        return m_val;
    }
    template<typename I>
    constexpr modint pow(I n) const
    {
        return power(*this, n);
    }
    constexpr modint inv() const
    {
        return pow(mod() - 2);
    }
    static modint sinv(u32 n)
    {
        static Vec<modint> is{1, 1};
        for (u32 i = (u32)is.size(); i <= n; i++) {
            is.push_back(-is[mod() % i] * (mod() / i));
        }
        return is[n];
    }
    static modint fact(u32 n)
    {
        static Vec<modint> fs{1, 1};
        for (u32 i = (u32)fs.size(); i <= n; i++) {
            fs.push_back(fs.back() * i);
        }
        return fs[n];
    }
    static modint ifact(u32 n)
    {
        static Vec<modint> ifs{1, 1};
        for (u32 i = (u32)ifs.size(); i <= n; i++) {
            ifs.push_back(ifs.back() * sinv(i));
        }
        return ifs[n];
    }
    static modint comb(int n, int k)
    {
        return k > n or k < 0 ? modint{0} : fact(n) * ifact(n - k) * ifact(k);
    }
private:
    static constexpr u32 norm(u32 x)
    {
        return x < mod() ? x : x - mod();
    }
    static constexpr u32 normll(i64 x)
    {
        return norm(u32(x % (i64)mod() + (i64)mod()));
    }
    u32 m_val;
};
using modint_1000000007 = modint<1000000007, 5, 1>;
using modint_998244353 = modint<998244353, 3, 23>;
template<int id>
using modint_dynamic = modint<0, 0, id>;
template<typename T = int>
class Graph
{
    struct Edge
    {
        Edge() = default;
        Edge(int i, int t, T c) : id{i}, to{t}, cost{c} {}
        int id;
        int to;
        T cost;
        operator int() const
        {
            return to;
        }
    };
public:
    Graph(int n) : m_v{n}, m_edges(n) {}
    void addEdge(int u, int v, bool bi = false)
    {
        assert(0 <= u and u < m_v);
        assert(0 <= v and v < m_v);
        m_edges[u].emplace_back(m_e, v, 1);
        if (bi) { m_edges[v].emplace_back(m_e, u, 1); }
        m_e++;
    }
    void addEdge(int u, int v, const T& c, bool bi = false)
    {
        assert(0 <= u and u < m_v);
        assert(0 <= v and v < m_v);
        m_edges[u].emplace_back(m_e, v, c);
        if (bi) { m_edges[v].emplace_back(m_e, u, c); }
        m_e++;
    }
    const Vec<Edge>& operator[](const int u) const
    {
        assert(0 <= u and u < m_v);
        return m_edges[u];
    }
    Vec<Edge>& operator[](const int u)
    {
        assert(0 <= u and u < m_v);
        return m_edges[u];
    }
    int v() const
    {
        return m_v;
    }
    int e() const
    {
        return m_e;
    }
    friend Ostream& operator<<(Ostream& os, const Graph& g)
    {
        for (int u : rep(g.v())) {
            for (const auto& [id, v, c] : g[u]) {
                os << "[" << id << "]: ";
                os << u << "->" << v << "(" << c << ")\n";
            }
        }
        return os;
    }
    Vec<T> sizes(int root = 0) const
    {
        const int N = v();
        assert(0 <= root and root < N);
        Vec<T> ss(N, 1);
        Fix([&](auto dfs, int u, int p) -> void {
            for (const auto& [id, v, c] : m_edges[u]) {
                static_cast<void>(id);
                if (v == p) { continue; }
                dfs(v, u);
                ss[u] += ss[v];
            }
        })(root, -1);
        return ss;
    }
    Vec<T> depths(int root = 0) const
    {
        const int N = v();
        assert(0 <= root and root < N);
        Vec<T> ds(N, 0);
        Fix([&](auto dfs, int u, int p) -> void {
            for (const auto& [id, v, c] : m_edges[u]) {
                static_cast<void>(id);
                if (v == p) { continue; }
                ds[v] = ds[u] + c;
                dfs(v, u);
            }
        })(root, -1);
        return ds;
    }
    Vec<int> parents(int root = 0) const
    {
        const int N = v();
        assert(0 <= root and root < N);
        Vec<int> ps(N, -1);
        Fix([&](auto dfs, int u, int p) -> void {
            for (const auto& [id, v, c] : m_edges[u]) {
                static_cast<void>(id);
                if (v == p) { continue; }
                ps[v] = u;
                dfs(v, u);
            }
        })(root, -1);
        return ps;
    }
private:
    int m_v;
    int m_e = 0;
    Vec<Vec<Edge>> m_edges;
};
#pragma endregion
int main()
{
    const auto N = in.val<int>();
    auto enc = [&](int z, int o) { return z + o * (N + 1); };
    auto dec = [&](int x) { return std::make_pair(x % (N + 1), x / (N + 1)); };
    const int K = (N + 1) * (N + 1);
    using mint = modint_998244353;
    auto mat = ndVec<mint>({K, K}, 0);
    auto b = Vec<mint>(K, 0);
    const mint ITOTAL = mint::comb(N, 2).inv();
    for (int x : rep(K)) {
        mat[x][x] = 1;
        b[x] = 1;
        const auto [z, o] = dec(x);
        const auto t = N - z - o;
        if (z == 0 and o == 0) {
            b[x] = 0;
        } else {
            for (int zd : rep(3)) {
                if (zd > z) { continue; }
                for (int od : rep(3)) {
                    if (od > o) { continue; }
                    const int td = 2 - zd - od;
                    if (td < 0) { continue; }
                    if (td > t) { continue; }
                    const mint p = mint::comb(z, zd) * mint::comb(o, od)
                                   * mint::comb(t, td) * ITOTAL;
                    const int nz = z - zd + td;
                    const int no = o - od + zd;
                    mat[x][enc(nz, no)] += -p;
                }
            }
        }
    }
    void(0);
    for (int i : rep(K)) {
        int piv = i;
        for (; piv < K; piv++) {
            if (mat[piv][i] != 0) { break; }
        }
        if (piv == K) { continue; }
        std::swap(mat[i], mat[piv]);
        std::swap(b[i], b[piv]);
        const mint iA = mat[i][i].inv();
        for (int j : rep(K)) {
            mat[i][j] *= iA;
        }
        b[i] *= iA;
        for (int k : rep(K)) {
            if (k == i) { continue; }
            const mint c = mat[k][i];
            for (int j : rep(K)) {
                mat[k][j] -= mat[i][j] * c;
            }
            b[k] -= b[i] * c;
        }
    }
    void(0);
    void(0);
    out.ln(b[N]);
    return 0;
}
accuracy: 1
time_ms: 140
memory_kb: 4268
final_rank: 19

code_snippet: #pragma GCC target("avx")
#pragma GCC optimize("O3")
#pragma GCC optimize("unroll-loops")
#include<bits/stdc++.h>
// #include<ext/pb_ds/assoc_container.hpp>
// #include<ext/pb_ds/tree_policy.hpp>
// #include<ext/pb_ds/tag_and_trait.hpp>
// using namespace __gnu_pbds;
// #include<boost/multiprecision/cpp_int.hpp>
// namespace multiprecisioninteger = boost::multiprecision;
// using cint=multiprecisioninteger::cpp_int;
using namespace std;
using ll=long long;
using datas=pair<ll,ll>;
using ddatas=pair<long double,long double>;
using tdata=pair<ll,datas>;
using vec=vector<ll>;
using mat=vector<vec>;
using pvec=vector<datas>;
using pmat=vector<pvec>;
// using llset=tree<ll,null_type,less<ll>,rb_tree_tag,tree_order_statistics_node_update>;
#define For(i,a,b) for(i=a;i<(ll)b;++i)
#define bFor(i,b,a) for(i=b,--i;i>=(ll)a;--i)
#define rep(i,N) For(i,0,N)
#define rep1(i,N) For(i,1,N)
#define brep(i,N) bFor(i,N,0)
#define brep1(i,N) bFor(i,N,1)
#define all(v) (v).begin(),(v).end()
#define allr(v) (v).rbegin(),(v).rend()
#define vsort(v) sort(all(v))
#define vrsort(v) sort(allr(v))
#define uniq(v) vsort(v),(v).erase(unique(all(v)),(v).end())
#define endl "\n"
#define popcount __builtin_popcountll
#define eb emplace_back
#define print(x) cout<<x<<endl
#define printyes print("Yes")
#define printno print("No")
#define printYES print("YES")
#define printNO print("NO")
#define output(v) do{bool f=0;for(auto outi:v){cout<<(f?" ":"")<<outi;f=1;}cout<<endl;}while(0)
#define matoutput(v) do{for(auto outimat:v)output(outimat);}while(0)
// constexpr ll mod=1000000007;
constexpr ll mod=998244353;
constexpr ll inf=1LL<<60;
constexpr long double eps=1e-9;
const long double PI=acosl(-1);
template<class T,class E> ostream& operator<<(ostream& os,const pair<T,E>& p){return os<<"("<<p.first<<","<<p.second<<")";}
template<class T> ostream& operator<<(ostream& os,const vector<T>& v){
  os<<"{";bool f=false;
  for(auto& x:v){if(f)os<<",";os<<x;f=true;}
  os<<"}";
  return os;
}
template<class T> ostream& operator<<(ostream& os,const set<T>& v){
  os<<"{";bool f=false;
  for(auto& x:v){if(f)os<<",";os<<x;f=true;}
  os<<"}";
  return os;
}
template<class T> ostream& operator<<(ostream& os,const multiset<T>& v){
  os<<"{";bool f=false;
  for(auto& x:v){if(f)os<<",";os<<x;f=true;}
  os<<"}";
  return os;
}
template<class T,class E> ostream& operator<<(ostream& os,const map<T,E>& v){
  os<<"{";bool f=false;
  for(auto& x:v){if(f)os<<",";os<<x;f=true;}
  os<<"}";
  return os;
}
template<class T> inline bool chmax(T& a,const T b){bool x=a<b;if(x)a=b;return x;}
template<class T> inline bool chmin(T& a,const T b){bool x=a>b;if(x)a=b;return x;}
#ifdef DEBUG
void debugg(){cout<<endl;}
template<class T,class... Args>void debugg(const T& x,const Args&... args){cout<<" "<<x;debugg(args...);}
#define debug(...) cout<<__LINE__<<" ["<<#__VA_ARGS__<<"]:",debugg(__VA_ARGS__)
#else
#define debug(...) (void(0))
#endif

inline void startupcpp(void) noexcept{
  cin.tie(0);
  ios::sync_with_stdio(false);
  cout<<fixed<<setprecision(15);
}

ll modinv(ll a,const ll m=mod) noexcept{
  ll b=m,u=1,v=0,t;
  while(b){
    t=a/b;
    a-=t*b; swap(a,b);
    u-=t*v; swap(u,v);
  }
  return (u+m)%m;
}

ll moddevide(const ll a,const ll b) noexcept{return (a*modinv(b))%mod;}

vec modncrlistp,modncrlistm;

ll modncr(const ll n,const ll r) noexcept{
  if(n<r||r<0)return 0;
  ll i,size=modncrlistp.size();
  if(size<=n){
    modncrlistp.resize(n+1);
    modncrlistm.resize(n+1);
    if(!size){
      modncrlistp[0]=modncrlistm[0]=1;
      size++;
    }
    For(i,size,n+1)modncrlistp[i]=modncrlistp[i-1]*i%mod;
    modncrlistm[n]=modinv(modncrlistp[n]);
    for(i=n;i>size;--i)modncrlistm[i-1]=modncrlistm[i]*i%mod;
  }
  return modncrlistp[n]*modncrlistm[r]%mod*modncrlistm[n-r]%mod;
}

ll modpow(ll a,ll n,const ll m=mod){
  if(n<0)return 0;
  ll res=1;
  while(n>0){
    if(n&1)res=res*a%m;
    a=a*a%m;
    n>>=1;
  }
  return res;
}

constexpr ll gcd(const ll a,const ll b) noexcept{return (!b)?abs(a):(a%b==0)?abs(b):gcd(b,a%b);}
constexpr ll lcm(const ll a,const ll b) noexcept{return a/gcd(a,b)*b;}

class modmatrix{
  mat a;
  ll H,W;
public:
  modmatrix(mat& g):a(g){
    H=g.size();
    W=g[0].size();
  }
  modmatrix(ll i,ll j):a(i,vec(j,0)){H=i;W=j;}
  modmatrix(ll n):a(n,vec(n,0)){H=W=n;}
  inline vec& operator [](int k){
    return a[k];
  }
  auto begin(){return a.begin();}
  auto end(){return a.end();}
  modmatrix operator +=(modmatrix b){
    ll i,j;
    rep(i,this->H)rep(j,this->W){
      (*this)[i][j]+=b[i][j];
      if((*this)[i][j]>=mod)(*this)[i][j]-=mod;
    }
    return (*this);
  }
  modmatrix operator -=(modmatrix b){
    ll i,j;
    rep(i,this->H)rep(j,this->W){
      (*this)[i][j]-=b[i][j];
      if((*this)[i][j]<0)(*this)[i][j]+=mod;
    }
    return (*this);
  }
  modmatrix operator *=(modmatrix b){
    ll i,j,k;
    assert(this->W==b.H);
    modmatrix c(this->H,b.W);
    rep(i,this->H){
      rep(k,this->W)rep(j,b.W){
        c[i][j]+=(*this)[i][k]*b[k][j]%mod;
      }
      rep(j,b.W)c[i][j]%=mod;
    }
    (*this)=c;
    return (*this);
  }
  modmatrix operator ^=(ll K){
    assert(this->H==this->W);
    modmatrix c(this->H);
    ll i;
    rep(i,this->H)c[i][i]=1;
    if(K&1)c*=(*this);
    while(K){
      K>>=1;
      (*this)*=(*this);
      if(K&1)c*=(*this);
    }
    this->a.swap(c.a);
    return (*this);
  }
  modmatrix operator +(modmatrix c){
    return modmatrix(*this)+=c;
  }
  modmatrix operator -(modmatrix c){
    return modmatrix(*this)-=c;
  }
  modmatrix operator *(modmatrix c){
    return modmatrix(*this)*=c;
  }
  modmatrix operator ^(ll K){
    return modmatrix(*this)^=K;
  }
  modmatrix del(ll eh,ll ew){
    ll i,j;
    mat res;
    rep(i,H){
      if(i==eh)continue;
      res.resize(res.size()+1);
      rep(j,W){
        if(j==ew)continue;
        res.back().eb(a[i][j]);
      }
    }
    return res;
  }
  ll determinant(){
    assert(H==W);
    ll i,j,k,ans=1;
    auto b(a);
    rep(i,H){
      if(!b[i][i]){
        For(j,i+1,H)if(b[j][i]){
          swap(b[i],b[j]);
          ans*=-1;
          break;
        }
        if(j==H)return 0;
      }
      (ans*=b[i][i])%=mod;
      For(j,i+1,W)(b[i][j]*=modinv(b[i][i]))%=mod;
      For(j,i+1,H)if(b[j][i]){
        ll x=mod-b[j][i];
        b[j][i]=0;
        For(k,i+1,W){
          b[j][k]+=x*b[i][k]%mod;
          if(b[j][k]>=mod)b[j][k]-=mod;
        }
      }
    }
    if(ans<0)ans+=mod;
    return ans;
  }
  modmatrix inv(){
    assert(H==W);
    ll i,j,k;
    modmatrix b(a),c(H,W);
    rep(i,H)c[i][i]=1;
    rep(i,H){
      if(!b[i][i]){
        For(j,i+1,H)if(b[j][i]){
          swap(b[i],b[j]);
          swap(c[i],c[j]);
          break;
        }
        if(j==H)assert(false);
      }
      ll x=modinv(b[i][i]);
      rep(j,W){
        (b[i][j]*=x)%=mod;
        (c[i][j]*=x)%=mod;
      }
      rep(j,H){
        if(i==j)continue;
        if(b[j][i]){
          x=mod-b[j][i];
          rep(k,W){
            b[j][k]+=x*b[i][k]%mod;
            if(b[j][k]>=mod)b[j][k]-=mod;
            c[j][k]+=x*c[i][k]%mod;
            if(c[j][k]>=mod)c[j][k]-=mod;
          }
        }
      }
    }
    return c;
  }
  void out(){
    for(auto x:a)output(x);
  }
};

ll N,cnt;
map<vec,ll> id;
pmat g;
ll dfs(vec ch){
  auto itr=id.find(ch);
  if(itr!=id.end())return itr->second;
  ll nowid=cnt++;
  id[ch]=nowid;
  pvec nowedge;
  ll i,j;
  rep(i,3){
    rep(j,3){
      if(ch[i]==0||ch[j]==0||(i==j&&ch[i]==1))continue;
      ll p=moddevide(ch[i],N);
      if(i==j)p*=moddevide(ch[j]-1,N-1);
      else p*=moddevide(ch[j],N-1);
      ch[i]--;ch[j]--;
      ch[(i+1)%3]++;ch[(j+1)%3]++;
      nowedge.emplace_back(p%mod,dfs(ch));
      ch[i]++;ch[j]++;
      ch[(i+1)%3]--;ch[(j+1)%3]--;
    }
  }
  if(nowid>=ll(g.size()))g.resize(nowid+1);
  g[nowid].swap(nowedge);
  return nowid;
}
int main(){
  startupcpp();
  cin>>N;
  id[vec({0,0,N})]=cnt++;
  dfs({N,0,0});
  modmatrix sol(g.size()),solv(g.size(),1);
  ll i;
  rep(i,g.size()){
    sol[i][i]=solv[i][0]=1;
    for(auto&[p,nxt]:g[i]){
      (sol[i][nxt]+=mod-p)%=mod;
    }
  }
  solv[0][0]=0;
  print((sol.inv()*solv)[id[vec({N,0,0})]][0]);
}
accuracy: 1
time_ms: 40
memory_kb: 5204
final_rank: 14

code_snippet: #define _USE_MATH_DEFINES
#include <bits/stdc++.h>
using namespace std;
#define FOR(i,m,n) for(int i=(m);i<(n);++i)
#define REP(i,n) FOR(i,0,n)
#define ALL(v) (v).begin(),(v).end()
using ll = long long;
constexpr int INF = 0x3f3f3f3f;
constexpr long long LINF = 0x3f3f3f3f3f3f3f3fLL;
constexpr double EPS = 1e-8;
constexpr int MOD = 998244353;
constexpr int DY4[]{1, 0, -1, 0}, DX4[]{0, -1, 0, 1};
constexpr int DY8[]{1, 1, 0, -1, -1, -1, 0, 1};
constexpr int DX8[]{0, -1, -1, -1, 0, 1, 1, 1};
template <typename T, typename U>
inline bool chmax(T& a, U b) { return a < b ? (a = b, true) : false; }
template <typename T, typename U>
inline bool chmin(T& a, U b) { return a > b ? (a = b, true) : false; }
struct IOSetup {
  IOSetup() {
    std::cin.tie(nullptr);
    std::ios_base::sync_with_stdio(false);
    std::cout << fixed << setprecision(20);
  }
} iosetup;

template <int M>
struct MInt {
  unsigned int v;
  MInt() : v(0) {}
  MInt(const long long x) : v(x >= 0 ? x % M : x % M + M) {}
  static constexpr int get_mod() { return M; }
  static void set_mod(const int divisor) { assert(divisor == M); }
  static void init(const int x = 10000000) {
    inv(x, true);
    fact(x);
    fact_inv(x);
  }
  static MInt inv(const int n, const bool init = false) {
    // assert(0 <= n && n < M && std::__gcd(n, M) == 1);
    static std::vector<MInt> inverse{0, 1};
    const int prev = inverse.size();
    if (n < prev) {
      return inverse[n];
    } else if (init) {
      // "n!" and "M" must be disjoint.
      inverse.resize(n + 1);
      for (int i = prev; i <= n; ++i) {
        inverse[i] = -inverse[M % i] * (M / i);
      }
      return inverse[n];
    }
    int u = 1, v = 0;
    for (unsigned int a = n, b = M; b;) {
      const unsigned int q = a / b;
      std::swap(a -= q * b, b);
      std::swap(u -= q * v, v);
    }
    return u;
  }
  static MInt fact(const int n) {
    static std::vector<MInt> factorial{1};
    const int prev = factorial.size();
    if (n >= prev) {
      factorial.resize(n + 1);
      for (int i = prev; i <= n; ++i) {
        factorial[i] = factorial[i - 1] * i;
      }
    }
    return factorial[n];
  }
  static MInt fact_inv(const int n) {
    static std::vector<MInt> f_inv{1};
    const int prev = f_inv.size();
    if (n >= prev) {
      f_inv.resize(n + 1);
      f_inv[n] = inv(fact(n).v);
      for (int i = n; i > prev; --i) {
        f_inv[i - 1] = f_inv[i] * i;
      }
    }
    return f_inv[n];
  }
  static MInt nCk(const int n, const int k) {
    if (n < 0 || n < k || k < 0) return 0;
    return fact(n) * (n - k < k ? fact_inv(k) * fact_inv(n - k) :
                                  fact_inv(n - k) * fact_inv(k));
  }
  static MInt nPk(const int n, const int k) {
    return n < 0 || n < k || k < 0 ? 0 : fact(n) * fact_inv(n - k);
  }
  static MInt nHk(const int n, const int k) {
    return n < 0 || k < 0 ? 0 : (k == 0 ? 1 : nCk(n + k - 1, k));
  }
  static MInt large_nCk(long long n, const int k) {
    if (n < 0 || n < k || k < 0) return 0;
    inv(k, true);
    MInt res = 1;
    for (int i = 1; i <= k; ++i) {
      res *= inv(i) * n--;
    }
    return res;
  }
  MInt pow(long long exponent) const {
    MInt res = 1, tmp = *this;
    for (; exponent > 0; exponent >>= 1) {
      if (exponent & 1) res *= tmp;
      tmp *= tmp;
    }
    return res;
  }
  MInt& operator+=(const MInt& x) {
    if ((v += x.v) >= M) v -= M;
    return *this;
  }
  MInt& operator-=(const MInt& x) {
    if ((v += M - x.v) >= M) v -= M;
    return *this;
  }
  MInt& operator*=(const MInt& x) {
    v = static_cast<unsigned long long>(v) * x.v % M;
    return *this;
  }
  MInt& operator/=(const MInt& x) { return *this *= inv(x.v); }
  bool operator==(const MInt& x) const { return v == x.v; }
  bool operator!=(const MInt& x) const { return v != x.v; }
  bool operator<(const MInt& x) const { return v < x.v; }
  bool operator<=(const MInt& x) const { return v <= x.v; }
  bool operator>(const MInt& x) const { return v > x.v; }
  bool operator>=(const MInt& x) const { return v >= x.v; }
  MInt& operator++() {
    if (++v == M) v = 0;
    return *this;
  }
  MInt operator++(int) {
    const MInt res = *this;
    ++*this;
    return res;
  }
  MInt& operator--() {
    v = (v == 0 ? M - 1 : v - 1);
    return *this;
  }
  MInt operator--(int) {
    const MInt res = *this;
    --*this;
    return res;
  }
  MInt operator+() const { return *this; }
  MInt operator-() const { return MInt(v ? M - v : 0); }
  MInt operator+(const MInt& x) const { return MInt(*this) += x; }
  MInt operator-(const MInt& x) const { return MInt(*this) -= x; }
  MInt operator*(const MInt& x) const { return MInt(*this) *= x; }
  MInt operator/(const MInt& x) const { return MInt(*this) /= x; }
  friend std::ostream& operator<<(std::ostream& os, const MInt& x) {
    return os << x.v;
  }
  friend std::istream& operator>>(std::istream& is, MInt& x) {
    long long v;
    is >> v;
    x = MInt(v);
    return is;
  }
};
using ModInt = MInt<MOD>;

template <typename T>
struct Matrix {
  explicit Matrix(const int m, const int n, const T def = 0)
      : data(m, std::vector<T>(n, def)) {}

  int nrow() const { return data.size(); }
  int ncol() const { return data.front().size(); }

  Matrix pow(long long exponent) const {
    const int n = nrow();
    Matrix<T> res(n, n, 0), tmp = *this;
    for (int i = 0; i < n; ++i) {
      res[i][i] = 1;
    }
    for (; exponent > 0; exponent >>= 1) {
      if (exponent & 1) res *= tmp;
      tmp *= tmp;
    }
    return res;
  }

  inline const std::vector<T>& operator[](const int i) const { return data[i]; }
  inline std::vector<T>& operator[](const int i) { return data[i]; }

  Matrix& operator=(const Matrix& x) = default;

  Matrix& operator+=(const Matrix& x) {
    const int m = nrow(), n = ncol();
    for (int i = 0; i < m; ++i) {
      for (int j = 0; j < n; ++j) {
        data[i][j] += x[i][j];
      }
    }
    return *this;
  }

  Matrix& operator-=(const Matrix& x) {
    const int m = nrow(), n = ncol();
    for (int i = 0; i < m; ++i) {
      for (int j = 0; j < n; ++j) {
        data[i][j] -= x[i][j];
      }
    }
    return *this;
  }

  Matrix& operator*=(const Matrix& x) {
    const int m = nrow(), l = ncol(), n = x.ncol();
    std::vector<std::vector<T>> res(m, std::vector<T>(n, 0));
    for (int i = 0; i < m; ++i) {
      for (int k = 0; k < l; ++k) {
        for (int j = 0; j < n; ++j) {
          res[i][j] += data[i][k] * x[k][j];
        }
      }
    }
    data.swap(res);
    return *this;
  }

  Matrix operator+(const Matrix& x) const { return Matrix(*this) += x; }
  Matrix operator-(const Matrix& x) const { return Matrix(*this) -= x; }
  Matrix operator*(const Matrix& x) const { return Matrix(*this) *= x; }

 private:
  std::vector<std::vector<T>> data;
};

template <typename T>
int gauss_jordan(Matrix<T>* a, const T eps = 1e-8,
                 const bool is_extended = false) {
  const int m = a->nrow(), n = a->ncol();
  int rank = 0;
  for (int col = 0; col < (is_extended ? n - 1 : n); ++col) {
    int pivot = -1;
    T mx = eps;
    for (int row = rank; row < m; ++row) {
      const T abs = ((*a)[row][col] < 0 ? -(*a)[row][col] : (*a)[row][col]);
      if (abs > mx) {
        pivot = row;
        mx = abs;
      }
    }
    if (pivot == -1) continue;
    std::swap((*a)[rank], (*a)[pivot]);
    T tmp = (*a)[rank][col];
    for (int col2 = 0; col2 < n; ++col2) {
      (*a)[rank][col2] /= tmp;
    }
    for (int row = 0; row < m; ++row) {
      if (row != rank &&
          ((*a)[row][col] < 0 ? -(*a)[row][col] : (*a)[row][col]) > eps) {
        tmp = (*a)[row][col];
        for (int col2 = 0; col2 < n; ++col2) {
          (*a)[row][col2] -= (*a)[rank][col2] * tmp;
        }
      }
    }
    ++rank;
  }
  return rank;
}

template <typename T, typename U = double>
std::vector<U> linear_equation(const Matrix<T>& a, const std::vector<T>& b,
                               const U eps = 1e-8) {
  const int m = a.nrow(), n = a.ncol();
  Matrix<U> c(m, n + 1);
  for (int i = 0; i < m; ++i) {
    std::copy(a[i].begin(), a[i].end(), c[i].begin());
    c[i].back() = b[i];
  }
  const int rank = gauss_jordan(&c, eps, true);
  for (int row = rank; row < m; ++row) {
    if ((c[row].back() < 0 ? -c[row].back() : c[row].back()) > eps) {
      return std::vector<U>{};
    }
  }
  std::vector<U> res(n, 0);
  for (int i = 0, j = 0; i < rank; ++i) {
    while ((c[i][j] < 0 ? -c[i][j] : c[i][j]) <= eps) ++j;
    res[j++] = c[i].back();
  }
  return res;
}

int main() {
  int n; cin >> n;
  if (n == 2) {
    cout << 2 << '\n';
    return 0;
  }
  vector<tuple<int, int, int>> states;
  map<tuple<int, int, int>, int> inv;
  for (int i = 0; i <= n; ++i) {
    for (int j = 0; i + j <= n; ++j) {
      inv[{i, j, n - (i + j)}] = states.size();
      states.emplace_back(i, j, n - (i + j));
    }
  }
  const int m = states.size();
  Matrix<ModInt> a(m, m);
  vector<ModInt> b(m, 0);
  REP(i, m) {
    const auto [x, y, z] = states[i];
    if (z == n) {
      a[i][i] = 1;
    } else {
      a[i][i] = b[i] = ModInt::nCk(n, 2);
      if (x >= 2) a[i][inv[{x - 2, y + 2, z}]] -= ModInt::nCk(x, 2);
      if (y >= 2) a[i][inv[{x, y - 2, z + 2}]] -= ModInt::nCk(y, 2);
      if (z >= 2) a[i][inv[{x + 2, y, z - 2}]] -= ModInt::nCk(z, 2);
      if (x >= 1 && y >= 1) a[i][inv[{x - 1, y, z + 1}]] -= x * y;
      if (y >= 1 && z >= 1) a[i][inv[{x + 1, y - 1, z}]] -= y * z;
      if (z >= 1 && x >= 1) a[i][inv[{x, y + 1, z - 1}]] -= z * x;
    }
  }
  cout << linear_equation(a, b, ModInt(0))[inv[{n, 0, 0}]] << '\n';
  return 0;
}
accuracy: 1
time_ms: 10
memory_kb: 3808
final_rank: 1

code_snippet: #include <stdio.h>
#include <bits/stdc++.h>

#include <cassert>
#include <numeric>
#include <type_traits>

#ifdef _MSC_VER
#include <intrin.h>
#endif


#include <utility>

#ifdef _MSC_VER
#include <intrin.h>
#endif

namespace atcoder {

namespace internal {

constexpr long long safe_mod(long long x, long long m) {
    x %= m;
    if (x < 0) x += m;
    return x;
}

struct barrett {
    unsigned int _m;
    unsigned long long im;

    explicit barrett(unsigned int m) : _m(m), im((unsigned long long)(-1) / m + 1) {}

    unsigned int umod() const { return _m; }

    unsigned int mul(unsigned int a, unsigned int b) const {

        unsigned long long z = a;
        z *= b;
#ifdef _MSC_VER
        unsigned long long x;
        _umul128(z, im, &x);
#else
        unsigned long long x =
            (unsigned long long)(((unsigned __int128)(z)*im) >> 64);
#endif
        unsigned int v = (unsigned int)(z - x * _m);
        if (_m <= v) v += _m;
        return v;
    }
};

constexpr long long pow_mod_constexpr(long long x, long long n, int m) {
    if (m == 1) return 0;
    unsigned int _m = (unsigned int)(m);
    unsigned long long r = 1;
    unsigned long long y = safe_mod(x, m);
    while (n) {
        if (n & 1) r = (r * y) % _m;
        y = (y * y) % _m;
        n >>= 1;
    }
    return r;
}

constexpr bool is_prime_constexpr(int n) {
    if (n <= 1) return false;
    if (n == 2 || n == 7 || n == 61) return true;
    if (n % 2 == 0) return false;
    long long d = n - 1;
    while (d % 2 == 0) d /= 2;
    constexpr long long bases[3] = {2, 7, 61};
    for (long long a : bases) {
        long long t = d;
        long long y = pow_mod_constexpr(a, t, n);
        while (t != n - 1 && y != 1 && y != n - 1) {
            y = y * y % n;
            t <<= 1;
        }
        if (y != n - 1 && t % 2 == 0) {
            return false;
        }
    }
    return true;
}
template <int n> constexpr bool is_prime = is_prime_constexpr(n);

constexpr std::pair<long long, long long> inv_gcd(long long a, long long b) {
    a = safe_mod(a, b);
    if (a == 0) return {b, 0};

    long long s = b, t = a;
    long long m0 = 0, m1 = 1;

    while (t) {
        long long u = s / t;
        s -= t * u;
        m0 -= m1 * u;  // |m1 * u| <= |m1| * s <= b


        auto tmp = s;
        s = t;
        t = tmp;
        tmp = m0;
        m0 = m1;
        m1 = tmp;
    }
    if (m0 < 0) m0 += b / s;
    return {s, m0};
}

constexpr int primitive_root_constexpr(int m) {
    if (m == 2) return 1;
    if (m == 167772161) return 3;
    if (m == 469762049) return 3;
    if (m == 754974721) return 11;
    if (m == 998244353) return 3;
    int divs[20] = {};
    divs[0] = 2;
    int cnt = 1;
    int x = (m - 1) / 2;
    while (x % 2 == 0) x /= 2;
    for (int i = 3; (long long)(i)*i <= x; i += 2) {
        if (x % i == 0) {
            divs[cnt++] = i;
            while (x % i == 0) {
                x /= i;
            }
        }
    }
    if (x > 1) {
        divs[cnt++] = x;
    }
    for (int g = 2;; g++) {
        bool ok = true;
        for (int i = 0; i < cnt; i++) {
            if (pow_mod_constexpr(g, (m - 1) / divs[i], m) == 1) {
                ok = false;
                break;
            }
        }
        if (ok) return g;
    }
}
template <int m> constexpr int primitive_root = primitive_root_constexpr(m);

unsigned long long floor_sum_unsigned(unsigned long long n,
                                      unsigned long long m,
                                      unsigned long long a,
                                      unsigned long long b) {
    unsigned long long ans = 0;
    while (true) {
        if (a >= m) {
            ans += n * (n - 1) / 2 * (a / m);
            a %= m;
        }
        if (b >= m) {
            ans += n * (b / m);
            b %= m;
        }

        unsigned long long y_max = a * n + b;
        if (y_max < m) break;
        n = (unsigned long long)(y_max / m);
        b = (unsigned long long)(y_max % m);
        std::swap(m, a);
    }
    return ans;
}

}  // namespace internal

}  // namespace atcoder


#include <cassert>
#include <numeric>
#include <type_traits>

namespace atcoder {

namespace internal {

#ifndef _MSC_VER
template <class T>
using is_signed_int128 =
    typename std::conditional<std::is_same<T, __int128_t>::value ||
                                  std::is_same<T, __int128>::value,
                              std::true_type,
                              std::false_type>::type;

template <class T>
using is_unsigned_int128 =
    typename std::conditional<std::is_same<T, __uint128_t>::value ||
                                  std::is_same<T, unsigned __int128>::value,
                              std::true_type,
                              std::false_type>::type;

template <class T>
using make_unsigned_int128 =
    typename std::conditional<std::is_same<T, __int128_t>::value,
                              __uint128_t,
                              unsigned __int128>;

template <class T>
using is_integral = typename std::conditional<std::is_integral<T>::value ||
                                                  is_signed_int128<T>::value ||
                                                  is_unsigned_int128<T>::value,
                                              std::true_type,
                                              std::false_type>::type;

template <class T>
using is_signed_int = typename std::conditional<(is_integral<T>::value &&
                                                 std::is_signed<T>::value) ||
                                                    is_signed_int128<T>::value,
                                                std::true_type,
                                                std::false_type>::type;

template <class T>
using is_unsigned_int =
    typename std::conditional<(is_integral<T>::value &&
                               std::is_unsigned<T>::value) ||
                                  is_unsigned_int128<T>::value,
                              std::true_type,
                              std::false_type>::type;

template <class T>
using to_unsigned = typename std::conditional<
    is_signed_int128<T>::value,
    make_unsigned_int128<T>,
    typename std::conditional<std::is_signed<T>::value,
                              std::make_unsigned<T>,
                              std::common_type<T>>::type>::type;

#else

template <class T> using is_integral = typename std::is_integral<T>;

template <class T>
using is_signed_int =
    typename std::conditional<is_integral<T>::value && std::is_signed<T>::value,
                              std::true_type,
                              std::false_type>::type;

template <class T>
using is_unsigned_int =
    typename std::conditional<is_integral<T>::value &&
                                  std::is_unsigned<T>::value,
                              std::true_type,
                              std::false_type>::type;

template <class T>
using to_unsigned = typename std::conditional<is_signed_int<T>::value,
                                              std::make_unsigned<T>,
                                              std::common_type<T>>::type;

#endif

template <class T>
using is_signed_int_t = std::enable_if_t<is_signed_int<T>::value>;

template <class T>
using is_unsigned_int_t = std::enable_if_t<is_unsigned_int<T>::value>;

template <class T> using to_unsigned_t = typename to_unsigned<T>::type;

}  // namespace internal

}  // namespace atcoder


namespace atcoder {

namespace internal {

struct modint_base {};
struct static_modint_base : modint_base {};

template <class T> using is_modint = std::is_base_of<modint_base, T>;
template <class T> using is_modint_t = std::enable_if_t<is_modint<T>::value>;

}  // namespace internal

template <int m, std::enable_if_t<(1 <= m)>* = nullptr>
struct static_modint : internal::static_modint_base {
    using mint = static_modint;

  public:
    static constexpr int mod() { return m; }
    static mint raw(int v) {
        mint x;
        x._v = v;
        return x;
    }

    static_modint() : _v(0) {}
    template <class T, internal::is_signed_int_t<T>* = nullptr>
    static_modint(T v) {
        long long x = (long long)(v % (long long)(umod()));
        if (x < 0) x += umod();
        _v = (unsigned int)(x);
    }
    template <class T, internal::is_unsigned_int_t<T>* = nullptr>
    static_modint(T v) {
        _v = (unsigned int)(v % umod());
    }

    unsigned int val() const { return _v; }

    mint& operator++() {
        _v++;
        if (_v == umod()) _v = 0;
        return *this;
    }
    mint& operator--() {
        if (_v == 0) _v = umod();
        _v--;
        return *this;
    }
    mint operator++(int) {
        mint result = *this;
        ++*this;
        return result;
    }
    mint operator--(int) {
        mint result = *this;
        --*this;
        return result;
    }

    mint& operator+=(const mint& rhs) {
        _v += rhs._v;
        if (_v >= umod()) _v -= umod();
        return *this;
    }
    mint& operator-=(const mint& rhs) {
        _v -= rhs._v;
        if (_v >= umod()) _v += umod();
        return *this;
    }
    mint& operator*=(const mint& rhs) {
        unsigned long long z = _v;
        z *= rhs._v;
        _v = (unsigned int)(z % umod());
        return *this;
    }
    mint& operator/=(const mint& rhs) { return *this = *this * rhs.inv(); }

    mint operator+() const { return *this; }
    mint operator-() const { return mint() - *this; }

    mint pow(long long n) const {
        assert(0 <= n);
        mint x = *this, r = 1;
        while (n) {
            if (n & 1) r *= x;
            x *= x;
            n >>= 1;
        }
        return r;
    }
    mint inv() const {
        if (prime) {
            assert(_v);
            return pow(umod() - 2);
        } else {
            auto eg = internal::inv_gcd(_v, m);
            assert(eg.first == 1);
            return eg.second;
        }
    }

    friend mint operator+(const mint& lhs, const mint& rhs) {
        return mint(lhs) += rhs;
    }
    friend mint operator-(const mint& lhs, const mint& rhs) {
        return mint(lhs) -= rhs;
    }
    friend mint operator*(const mint& lhs, const mint& rhs) {
        return mint(lhs) *= rhs;
    }
    friend mint operator/(const mint& lhs, const mint& rhs) {
        return mint(lhs) /= rhs;
    }
    friend bool operator==(const mint& lhs, const mint& rhs) {
        return lhs._v == rhs._v;
    }
    friend bool operator!=(const mint& lhs, const mint& rhs) {
        return lhs._v != rhs._v;
    }

  private:
    unsigned int _v;
    static constexpr unsigned int umod() { return m; }
    static constexpr bool prime = internal::is_prime<m>;
};

template <int id> struct dynamic_modint : internal::modint_base {
    using mint = dynamic_modint;

  public:
    static int mod() { return (int)(bt.umod()); }
    static void set_mod(int m) {
        assert(1 <= m);
        bt = internal::barrett(m);
    }
    static mint raw(int v) {
        mint x;
        x._v = v;
        return x;
    }

    dynamic_modint() : _v(0) {}
    template <class T, internal::is_signed_int_t<T>* = nullptr>
    dynamic_modint(T v) {
        long long x = (long long)(v % (long long)(mod()));
        if (x < 0) x += mod();
        _v = (unsigned int)(x);
    }
    template <class T, internal::is_unsigned_int_t<T>* = nullptr>
    dynamic_modint(T v) {
        _v = (unsigned int)(v % mod());
    }

    unsigned int val() const { return _v; }

    mint& operator++() {
        _v++;
        if (_v == umod()) _v = 0;
        return *this;
    }
    mint& operator--() {
        if (_v == 0) _v = umod();
        _v--;
        return *this;
    }
    mint operator++(int) {
        mint result = *this;
        ++*this;
        return result;
    }
    mint operator--(int) {
        mint result = *this;
        --*this;
        return result;
    }

    mint& operator+=(const mint& rhs) {
        _v += rhs._v;
        if (_v >= umod()) _v -= umod();
        return *this;
    }
    mint& operator-=(const mint& rhs) {
        _v += mod() - rhs._v;
        if (_v >= umod()) _v -= umod();
        return *this;
    }
    mint& operator*=(const mint& rhs) {
        _v = bt.mul(_v, rhs._v);
        return *this;
    }
    mint& operator/=(const mint& rhs) { return *this = *this * rhs.inv(); }

    mint operator+() const { return *this; }
    mint operator-() const { return mint() - *this; }

    mint pow(long long n) const {
        assert(0 <= n);
        mint x = *this, r = 1;
        while (n) {
            if (n & 1) r *= x;
            x *= x;
            n >>= 1;
        }
        return r;
    }
    mint inv() const {
        auto eg = internal::inv_gcd(_v, mod());
        assert(eg.first == 1);
        return eg.second;
    }

    friend mint operator+(const mint& lhs, const mint& rhs) {
        return mint(lhs) += rhs;
    }
    friend mint operator-(const mint& lhs, const mint& rhs) {
        return mint(lhs) -= rhs;
    }
    friend mint operator*(const mint& lhs, const mint& rhs) {
        return mint(lhs) *= rhs;
    }
    friend mint operator/(const mint& lhs, const mint& rhs) {
        return mint(lhs) /= rhs;
    }
    friend bool operator==(const mint& lhs, const mint& rhs) {
        return lhs._v == rhs._v;
    }
    friend bool operator!=(const mint& lhs, const mint& rhs) {
        return lhs._v != rhs._v;
    }

  private:
    unsigned int _v;
    static internal::barrett bt;
    static unsigned int umod() { return bt.umod(); }
};
template <int id> internal::barrett dynamic_modint<id>::bt(998244353);

using modint998244353 = static_modint<998244353>;
using modint1000000007 = static_modint<1000000007>;
using modint = dynamic_modint<-1>;

namespace internal {

template <class T>
using is_static_modint = std::is_base_of<internal::static_modint_base, T>;

template <class T>
using is_static_modint_t = std::enable_if_t<is_static_modint<T>::value>;

template <class> struct is_dynamic_modint : public std::false_type {};
template <int id>
struct is_dynamic_modint<dynamic_modint<id>> : public std::true_type {};

template <class T>
using is_dynamic_modint_t = std::enable_if_t<is_dynamic_modint<T>::value>;

}  // namespace internal

}  // namespace atcoder

using namespace atcoder;
using mint = modint998244353;
using namespace std;
#define rep(i,n) for(int i=0;i<(n);i++)
#define Inf 1000000001

void Gauss(vector<vector<mint>> &A){
	if(A.size()==0)return;
	int last = 0;
	rep(i,A[0].size()){
		int ind = -1;
		for(int j=last;j<A.size();j++){
			if(A[j][i]!=0){
				ind = j;
				break;
			}
		}
		if(ind==-1)continue;
		swap(A[last],A[ind]);
		
		mint x = A[last][i].inv();
		rep(j,A[last].size())A[last][j] *= x;
		
		rep(j,A.size()){
			if(j==last)continue;
			x = A[j][i];
			if(x==0)continue;
			rep(k,A[j].size()){
				A[j][k] -= A[last][k] * x;
			}
		}
		
		last++;
	}
}

int n;

mint choose2(int a,int b){
	mint ret = a;
	ret *= b;
	mint x = n;
	x *= n-1;
	x /= 2;
	ret /= x;
	return ret;
}
mint choose2(int a){
	mint ret = a;
	ret *= a-1;
	ret/=2;
	mint x = n;
	x *= n-1;
	x /= 2;
	ret /= x;
	return ret;
}
bool f[30][30];
int main(){
	
	
	cin>>n;
	
	queue<pair<int,int>> Q;
	Q.emplace(n,0);
	f[n][0] = true;
	while(Q.size()>0){
		int x = Q.front().first;
		int y = Q.front().second;
		Q.pop();
		int z = n-x-y;
		rep(i,3){
			rep(j,3){
				rep(k,3){
					if(i+j+k!=2)continue;
					if(i>x||j>y||k>z)continue;
					int xx=  x-i;
					int yy = y-j;
					xx += k;
					yy += i;
					if(f[xx][yy])continue;
					f[xx][yy] = true;
					Q.emplace(xx,yy);
				}
			}
		}
	}
	
	vector<vector<mint>> a;
	int C = (n+1) * (n+1);
	rep(i,n+1){
		rep(j,n+1){
			if(i+j>n)break;
			vector<mint> t((n+1)*(n+1)+1,0);
			t[i*(n+1) + j] -= 1;
			if(f[i][j]==false)continue;
			if(i!=0 || j!=0){
				
				int x = i,y = j,z = n-i-j;
				if(x>=2){
					t[(i-2)*(n+1)+j+2] += choose2(x);
				}
				if(y>=2){
					t[i*(n+1)+j-2] += choose2(y);
				}
				if(z>=2){
					t[(i+2)*(n+1)+j] += choose2(z);
				}
				t[C] = 1;
				if(x>=1 && y>=1){
					t[(i-1)*(n+1)+j] += choose2(x,y);
				}
				if(y>=1&&z>=1){
					t[(i+1)*(n+1)+j-1] += choose2(y,z);
				}
				if(z>=1&&x>=1){
					t[i*(n+1)+j+1] += choose2(z,x);
				}
				
			}
			a.push_back(t);
			/*
			if(i==0&&j==2){
				rep(k,t.size()){
					cout<<t[k].val()<<',';
				}
				cout<<endl;
			}
			*/
		}
	}
	
	Gauss(a);
	
	rep(i,a.size()){
		if(a[i][(n+1)*n].val()==1){
			cout<<(-a[i].back()).val()<<endl;
			return 0;
		}
		/*
		rep(j,a[i].size()){
			cout<<a[i][j].val()<<',';
		}
		cout<<endl;
		*/
	}
	cout<<a[0][C].val()<<endl;
		
	
	return 0;
}
accuracy: 1
time_ms: 20
memory_kb: 3852
final_rank: 9

code_snippet: /*	author:  Kite_kuma
	created: 2022.03.19 14:23:37 */

// #ifdef LOCAL
// #define _GLIBCXX_DEBUG
// #endif
#include <bits/stdc++.h>
using namespace std;

#pragma region macros

#define foa(s, v) for(auto &s : v)
#define all(v) (v).begin(), (v).end()
#define rall(v) (v).rbegin(), (v).rend()
#define popcnt(n) __builtin_popcountll((long long)n)

#define REPname(a, b, c, d, e, ...) e
#define rep(...) REPname(__VA_ARGS__, REP3, REP2, REP1, REP0)(__VA_ARGS__)
#define REP0(x) for(int Counter_in_rep_macro = 0; Counter_in_rep_macro < (x); ++Counter_in_rep_macro)
#define REP1(i, x) for(int i = 0; i < (x); ++i)
#define REP2(i, l, r) for(int i = (l); i < (r); ++i)
#define REP3(i, l, r, c) for(int i = (l); i < (r); i += (c))

#define DREPname(a, b, c, d, e, ...) e
#define drep(...) DREPname(__VA_ARGS__, DREP3, DREP2, DREP1)(__VA_ARGS__)
#define DREP1(i, x) for(int i = (x)-1; i >= 0; --i)
#define DREP2(i, l, r) for(int i = (r)-1; i >= (l); --i)
#define DREP3(i, l, r, c) for(int i = (r)-1; i >= (l); i -= (c))

#pragma endregion

#pragma region aliases

using ll = long long;
using ld = long double;
using ull = unsigned long long;
using vi = std::vector<int>;
using vvi = std::vector<std::vector<int>>;
using vvvi = std::vector<std::vector<std::vector<int>>>;
using vll = std::vector<ll>;
using vvll = std::vector<vll>;
using vvvll = std::vector<vvll>;
using pii = std::pair<int, int>;
using pll = std::pair<long long, long long>;
template <class T = ll>
using V = std::vector<T>;
template <class T = ll>
using VV = V<V<T>>;
template <class T = ll>
using VVV = V<V<V<T>>>;
template <class T = ll>
using pqup = std::priority_queue<T, std::vector<T>, std::greater<T>>;
template <class T = ll>
using pqdn = std::priority_queue<T>;

#pragma endregion

#pragma region constants

const int inf = 1e9;
const long long INF = 1e18;
const long double pi = acos(-1);
const char dl = '\n';
const char sp = ' ';
int dx[8] = {1, 0, -1, 0, 1, -1, -1, 1};
int dy[8] = {0, 1, 0, -1, 1, 1, -1, -1};

const int mod_1000000007 = 1000000007;
const int mod_998244353 = 998244353;

#pragma endregion

#pragma region basic_operation

template <class T0, class T1, class T2>
inline bool in_range(T0 x, T1 lef, T2 rig) {
	return ((lef <= x) && (x < rig));
}

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

void Yes(bool f = 1) { std::cout << (f ? "Yes" : "No") << '\n'; }
void No() { std::cout << "No\n"; }
void YES(bool f = 1) { std::cout << (f ? "YES" : "NO") << '\n'; }
void NO() { std::cout << "NO\n"; }

template <class T>
void drop(T answer) {
	std::cout << answer << '\n';
	exit(0);
}

void err(bool flag = true) {
	if(!flag) return;
	std::cout << -1 << '\n';
	exit(0);
}

template <class T>
void vout(std::vector<T> const &v, bool vertically = 0) {
	if(vertically) {
		for(auto const &a : v) {
			std::cout << a << '\n';
		}
		return;
	}
	for(auto it = v.begin(); it != v.end(); it++) {
		std::cout << (*it);
		if(it != v.end() - 1) {
			std::cout << ' ';
		}
	}
	std::cout << '\n';
	return;
}

inline void print() { std::cout << '\n'; }
template <class T>
inline void print(T x) {
	std::cout << x << '\n';
	return;
}
template <typename Head, typename... Tail>
void print(Head H, Tail... T) {
	std::cout << H << " ";
	print(T...);
}

template <class T>
void add(std::vector<T> &v, T val) {
	for(auto &a : v) a += val;
	return;
}

template <class T>
T dup(T a, T b) {
	assert(b != 0);
	return (a + b - 1) / b;
}

template <class T>
T greatest_lower_multiple(T x, T d) {
	if(d == 0) return 0;
	if(d < 0) d *= -1;
	T y = x % d;
	if(y < 0) y += d;
	return x - y;
}

template <class T>
T least_upper_multiple(T x, T d) {
	return -greatest_lower_multiple(-x, d);
}

long long POW(long long a, long long n) {
	long long res = 1;
	while(n > 0) {
		if(n & 1) res = res * a;
		a = a * a;
		n >>= 1;
	}
	return res;
}

long long modpow(long long a, long long n, long long mod) {	 // a^n mod
	assert(n >= 0 && mod);
	if(mod == 1) return 0LL;
	long long res = 1;
	a %= mod;
	while(n > 0) {
		if(n & 1) res = res * a % mod;
		a = a * a % mod;
		n >>= 1;
	}
	return res < 0 ? res + mod : res;
}

// return x which satisfies a * x % mod == gcd(a, mod)
// not (mod | a)
long long modinv(long long a, long long mod) {
	long long b = mod, u = 1, v = 0;
	while(b) {
		long long t = a / b;
		a -= t * b;
		std::swap(a, b);
		u -= t * v;
		std::swap(u, v);
	}
	u %= mod;
	if(u < 0) u += mod;
	return u;
}

template <class T>
int lb(const std::vector<T> &a, const T x) {
	return std::distance(a.begin(), std::lower_bound(a.begin(), a.end(), x));
}
template <class T>
int ub(const std::vector<T> &a, const T x) {
	return std::distance(a.begin(), std::upper_bound(a.begin(), a.end(), x));
}
template <class T>
void unq_sort(std::vector<T> &a) {
	std::sort(a.begin(), a.end());
	a.erase(std::unique(a.begin(), a.end()), a.end());
}
template <class T>
std::vector<int> press(std::vector<T> &a) {
	auto vec = a;
	unq_sort(vec);
	std::vector<int> ret;
	for(auto &v : a) ret.push_back(lb(vec, v));
	return ret;
}

#pragma endregion

#pragma region input
#define VEC(type, name, size) \
	vector<type> name(size);  \
	scanner::INPUT(name)
#define VVEC(type, name, h, w)                     \
	vector<vector<type>> name(h, vector<type>(w)); \
	scanner::INPUT(name)
#define INT(...)     \
	int __VA_ARGS__; \
	scanner::INPUT(__VA_ARGS__)
#define LL(...)            \
	long long __VA_ARGS__; \
	scanner::INPUT(__VA_ARGS__)
#define STR(...)        \
	string __VA_ARGS__; \
	scanner::INPUT(__VA_ARGS__)
#define CHR(...)      \
	char __VA_ARGS__; \
	scanner::INPUT(__VA_ARGS__)
#define DBL(...)        \
	double __VA_ARGS__; \
	scanner::INPUT(__VA_ARGS__)
#define LD(...)              \
	long double __VA_ARGS__; \
	scanner::INPUT(__VA_ARGS__)
#define TPL3(type0, type1, type2, name)   \
	std::tuple<type0, type1, type2> name; \
	scanner::INPUT(name);
#define TPL4(type0, type1, type2, type3, name)   \
	std::tuple<type0, type1, type2, type3> name; \
	scanner::INPUT(name);

namespace scanner {
template <class T>
void scan(T &a) {
	std::cin >> a;
}

template <class T, class L>
void scan(std::pair<T, L> &p) {
	scan(p.first);
	scan(p.second);
}

template <class T0, class T1, class T2>
void scan(std::tuple<T0, T1, T2> &p) {
	T0 t0;
	T1 t1;
	T2 t2;
	scan(t0);
	scan(t1);
	scan(t2);
	p = std::make_tuple(t0, t1, t2);
}

template <class T0, class T1, class T2, class T3>
void scan(std::tuple<T0, T1, T2, T3> &p) {
	T0 t0;
	T1 t1;
	T2 t2;
	T3 t3;
	scan(t0);
	scan(t1);
	scan(t2);
	scan(t3);
	p = std::make_tuple(t0, t1, t2, t3);
}

template <class T>
void scan(std::vector<T> &a) {
	for(auto &i : a) scan(i);
}

void INPUT() {}
template <class Head, class... Tail>
void INPUT(Head &head, Tail &... tail) {
	scan(head);
	INPUT(tail...);
}
}  // namespace scanner

template <typename T1, typename T2>
std::istream &operator>>(std::istream &is, std::pair<T1, T2> &p) {
	is >> p.first >> p.second;
	return is;
}
#pragma endregion

#pragma region debug

#pragma region output
template <typename T1, typename T2>
std::ostream &std::operator<<(std::ostream &os, const std::pair<T1, T2> &p) {
	os << p.first << " " << p.second;
	return os;
}
template <class T>
std::ostream &std::operator<<(std::ostream &os, const std::vector<T> &v) {
	for(int i = 0; i < (int)v.size(); i++) {
		if(i) os << " ";
		os << v[i];
	}
	return os;
}
#pragma endregion

#pragma region view

namespace viewer {
void view(const long long e) {
	if(e == INF)
		std::cerr << "INF";
	else if(e == -INF)
		std::cerr << "-INF";
	else
		std::cerr << e;
}

void view(const int e) {
	if(e == inf)
		std::cerr << "inf";
	else if(e == -inf)
		std::cerr << "-inf";
	else
		std::cerr << e;
}

template <typename T>
void view(const T e) {
	std::cerr << e;
}

template <typename T, typename U>
void view(const std::pair<T, U> &p) {
	std::cerr << "(";
	view(p.first);
	std::cerr << ", ";
	view(p.second);
	std::cerr << ")";
}

template <class T0, class T1, class T2>
void view(const std::tuple<T0, T1, T2> &p) {
	std::cerr << "(";
	view(std::get<0>(p));
	std::cerr << ", ";
	view(std::get<1>(p));
	std::cerr << ", ";
	view(std::get<2>(p));
	std::cerr << ")";
}

template <class T0, class T1, class T2, class T3>
void view(const std::tuple<T0, T1, T2, T3> &p) {
	std::cerr << "(";
	view(std::get<0>(p));
	std::cerr << ", ";
	view(std::get<1>(p));
	std::cerr << ", ";
	view(std::get<2>(p));
	std::cerr << ", ";
	view(std::get<3>(p));
	std::cerr << ")";
}

template <typename T>
void view(const std::set<T> &s) {
	if(s.empty()) {
		std::cerr << "{ }";
		return;
	}
	std::cerr << "{ ";
	for(auto &t : s) {
		view(t);
		std::cerr << ", ";
	}
	std::cerr << "\b\b }";
}

template <typename T>
void view(const std::unordered_set<T> &s) {
	if(s.empty()) {
		std::cerr << "{ }";
		return;
	}
	std::cerr << "{ ";
	for(auto &t : s) {
		view(t);
		std::cerr << ", ";
	}
	std::cerr << "\b\b }";
}

template <typename T>
void view(const std::vector<T> &v) {
	if(v.empty()) {
		std::cerr << "{ }";
		return;
	}
	std::cerr << "{ ";
	for(const auto &e : v) {
		view(e);
		std::cerr << ", ";
	}
	std::cerr << "\b\b }";
}

template <typename T>
void view(const std::vector<std::vector<T>> &vv) {
	std::cerr << "{\n";
	for(const auto &v : vv) {
		std::cerr << "\t";
		view(v);
		std::cerr << '\n';
	}
	std::cerr << "}";
}

template <typename T, typename U>
void view(const std::vector<std::pair<T, U>> &v) {
	std::cerr << "{\n";
	for(const auto &c : v) {
		std::cerr << "\t(";
		view(c.first);
		std::cerr << ", ";
		view(c.second);
		std::cerr << ")\n";
	}
	std::cerr << "}";
}

template <class T0, class T1, class T2>
void view(const std::vector<std::tuple<T0, T1, T2>> &v) {
	if(v.empty()) {
		std::cerr << "{ }";
		return;
	}
	std::cerr << '{';
	for(const auto &t : v) {
		std::cerr << "\n\t";
		view(t);
		std::cerr << ",";
	}
	std::cerr << "\n}";
}

template <class T0, class T1, class T2, class T3>
void view(const std::vector<std::tuple<T0, T1, T2, T3>> &v) {
	if(v.empty()) {
		std::cerr << "{ }";
		return;
	}
	std::cerr << '{';
	for(const auto &t : v) {
		std::cerr << "\n\t";
		view(t);
		std::cerr << ",";
	}
	std::cerr << "\n}";
}

template <typename T, typename U>
void view(const std::map<T, U> &m) {
	std::cerr << "{\n";
	for(const auto &t : m) {
		std::cerr << "\t[";
		view(t.first);
		std::cerr << "] : ";
		view(t.second);
		std::cerr << '\n';
	}
	std::cerr << "}";
}

template <typename T, typename U>
void view(const std::unordered_map<T, U> &m) {
	std::cerr << "{\n";
	for(const auto &t : m) {
		std::cerr << "\t[";
		view(t.first);
		std::cerr << "] : ";
		view(t.second);
		std::cerr << '\n';
	}
	std::cerr << "}";
}
}  // namespace viewer

#pragma endregion

// when debugging : g++ foo.cpp -DLOCAL
#ifdef LOCAL
void debug_out() {}
template <typename Head, typename... Tail>
void debug_out(Head H, Tail... T) {
	viewer::view(H);
	std::cerr << ", ";
	debug_out(T...);
}
#define debug(...)                                                \
	do {                                                          \
		std::cerr << __LINE__ << " [" << #__VA_ARGS__ << "] : ["; \
		debug_out(__VA_ARGS__);                                   \
		std::cerr << "\b\b]\n";                                   \
	} while(0)
#define dump(x)                                      \
	do {                                             \
		std::cerr << __LINE__ << " " << #x << " : "; \
		viewer::view(x);                             \
		std::cerr << '\n';                           \
	} while(0)

#else
#define debug(...) (void(0))
#define dump(x) (void(0))
#endif

#pragma endregion

#pragma region modint

const int mod = mod_998244353;

template <int mod>
struct ModInt {
	int x;
	ModInt() : x(0) {}
	ModInt(long long y) : x(y >= 0 ? y % mod : (mod - (-y) % mod) % mod) {}
	ModInt &operator+=(const ModInt &p) {
		if((x += p.x) >= mod) x -= mod;
		return *this;
	}
	ModInt &operator-=(const ModInt &p) {
		if((x += mod - p.x) >= mod) x -= mod;
		return *this;
	}
	ModInt &operator*=(const ModInt &p) {
		x = (int)(1LL * x * p.x % mod);
		return *this;
	}
	ModInt &operator/=(const ModInt &p) {
		*this *= p.inverse();
		return *this;
	}
	ModInt operator-() const { return ModInt(-x); }
	ModInt operator+(const ModInt &p) const { return ModInt(*this) += p; }
	ModInt operator-(const ModInt &p) const { return ModInt(*this) -= p; }
	ModInt operator*(const ModInt &p) const { return ModInt(*this) *= p; }
	ModInt operator/(const ModInt &p) const { return ModInt(*this) /= p; }
	bool operator==(const ModInt &p) const { return x == p.x; }
	bool operator!=(const ModInt &p) const { return x != p.x; }
	ModInt inverse() const {
		int a = x, b = mod, u = 1, v = 0, t;
		while(b > 0) {
			t = a / b;
			swap(a -= t * b, b);
			swap(u -= t * v, v);
		}
		return ModInt(u);
	}
	ModInt pow(long long n) const {
		ModInt ret(1), mul(x);
		while(n > 0) {
			if(n & 1) ret *= mul;
			mul *= mul;
			n >>= 1;
		}
		return ret;
	}
	friend ostream &operator<<(ostream &os, const ModInt &p) { return os << p.x; }
	friend istream &operator>>(istream &is, ModInt &a) {
		long long t;
		is >> t;
		a = ModInt<mod>(t);
		return (is);
	}
	static int get_mod() { return mod; }
};
using mint = ModInt<mod>;

#pragma endregion

mint solve(int n) {
	vector<pair<int, int>> states;
	rep(zero, n + 1) rep(one, n - zero + 1) { states.emplace_back(zero, one); }
	sort(all(states));
	auto id = [&states](int zero, int one) { return lb(states, pair(zero, one)); };
	const mint prob = mint(n * (n - 1) / 2).inverse();
	const int matsize = states.size();
	// n = matsize;
	VV<mint> mat(matsize, V<mint>(matsize + 1));
	V<mint> vec(matsize, -1);
	const int end_id = id(0, 0);
	vec[end_id] = 0;
	rep(state_id, matsize) {
		if(end_id == state_id) continue;
		int one, zero;
		tie(zero, one) = states[state_id];
		int two = n - one - zero;
		const vector<int> counts = {zero, one, two};
		rep(first, 3) rep(second, first, 3) {
			vector<int> seq = counts;
			seq[first]--;
			seq[second]--;
			if(*min_element(all(seq)) < 0) continue;
			seq[(first + 1) % 3]++;
			seq[(second + 1) % 3]++;
			mint p = prob;
			if(second == first) {
				p *= counts[first];
				p *= counts[first] - 1;
				p /= 2;
			} else {
				p *= counts[first];
				p *= counts[second];
			}
			mat[state_id][id(seq[0], seq[1])] += p;
		}
		mat[state_id][state_id] -= 1;
	}
	mat[end_id][end_id] = 1;

	rep(i, matsize) mat[i].back() = vec[i];

	debug(mat);


	rep(i, matsize) {
		rep(row, i, matsize) {
			if(mat[row][i].x) {
				swap(mat[i], mat[row]);
				break;
			}
		}
		const mint inv = mat[i][i].inverse();
		rep(j, matsize + 1) mat[i][j] *= inv;

		rep(row, matsize) {
			if(row == i) continue;
			const mint mult = mat[row][i];
			rep(j, matsize + 1) { mat[row][j] -= mult * mat[i][j]; }
		}
		// if(n < 3) debug(mat);
	}

	debug(mat);

	const int start_id = id(n, 0);
	return mat[start_id].back();
}
int main() {
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);
	std::cout << std::fixed << std::setprecision(15);
	srand((unsigned)time(NULL));

	int n;
	while(cin >> n && n) cout << solve(n) << dl;

	return 0;
}
accuracy: 1
time_ms: 20
memory_kb: 3608
final_rank: 6

code_snippet: #include <bits/stdc++.h>
using namespace std;
const long long MOD = 998244353;
long long modpow(long long a, long long b){
	long long ans = 1;
	while (b > 0){
		if (b % 2 == 1){
			ans *= a;
			ans %= MOD;
		}
		a *= a;
		a %= MOD;
		b /= 2;
	}
	return ans;
}
long long modinv(long long a){
	return modpow(a, MOD - 2);
}
int main(){
  int N;
  cin >> N;
  vector<int> a, b, c;
  vector<vector<int>> id(N + 1, vector<int>(N + 1));
  int M = 0;
  for (int i = 0; i <= N; i++){
    for (int j = 0; j <= N - i; j++){
      a.push_back(i);
      b.push_back(j);
      c.push_back(N - i - j);
      id[i][j] = M;
      M++;
    }
  }
  long long X = modinv(N * (N - 1) / 2);
  X = MOD - X;
  vector<vector<long long>> P(M, vector<long long>(M + 1, 0));
  for (int i = 0; i < M; i++){
    P[i][i] = 1;
    if (i > 0){
      P[i][M] = 1;
      if (a[i] >= 2){
        long long p = a[i] * (a[i] - 1) / 2 * X % MOD;
        P[i][id[a[i] - 2][b[i] + 2]] += p;
      }
      if (b[i] >= 2){
        long long p = b[i] * (b[i] - 1) / 2 * X % MOD;
        P[i][id[a[i]][b[i] - 2]] += p;
      }
      if (c[i] >= 2){
        long long p = c[i] * (c[i] - 1) / 2 * X % MOD;
        P[i][id[a[i] + 2][b[i]]] += p;
      }
      if (a[i] >= 1 && b[i] >= 1){
        long long p = a[i] * b[i] * X % MOD;
        P[i][id[a[i] - 1][b[i]]] += p;
      }
      if (b[i] >= 1 && c[i] >= 1){
        long long p = b[i] * c[i] * X % MOD;
        P[i][id[a[i] + 1][b[i] - 1]] += p;
      }
      if (c[i] >= 1 && a[i] >= 1){
        long long p = c[i] * a[i] * X % MOD;
        P[i][id[a[i]][b[i] + 1]] += p;
      }
    }
  }
  for (int i = 0; i < M; i++){
    int p = i;
    for (int j = i + 1; j < M; j++){
      if (P[j][i] != 0){
        p = j;
      }
    }
    swap(P[p], P[i]);
    if (P[i][i] != 0){
      for (int j = i + 1; j <= M; j++){
        P[i][j] *= modinv(P[i][i]);
        P[i][j] %= MOD;
      }
      for (int j = 0; j < M; j++){
        if (i != j){
          for (int k = i + 1; k <= M; k++){
            P[j][k] += MOD - P[j][i] * P[i][k] % MOD;
            P[j][k] %= MOD;
          }
        }
      }
    }
  }
  cout << P[id[N][0]][M] << endl;
}
accuracy: 1
time_ms: 10
memory_kb: 3864
final_rank: 3

code_snippet: #pragma GCC optimize("O3")
#pragma GCC optimize("unroll-loops")
#include<iostream>
#include<string>
#include<cstdio>
#include<vector>
#include<cmath>
#include<algorithm>
#include<functional>
#include<iomanip>
#include<queue>
#include<ciso646>
#include<random>
#include<map>
#include<set>
#include<bitset>
#include<stack>
#include<unordered_map>
#include<unordered_set>
#include<utility>
#include<cassert>
#include<complex>
#include<numeric>
#include<array>
#include<chrono>
using namespace std;

//#define int long long
typedef long long ll;

typedef unsigned long long ul;
typedef unsigned int ui;
constexpr ll mod = 998244353;
//constexpr ll mod = 1000000007;
const ll INF = mod * mod;
typedef pair<int, int>P;

#define rep(i,n) for(int i=0;i<n;i++)
#define per(i,n) for(int i=n-1;i>=0;i--)
#define Rep(i,sta,n) for(int i=sta;i<n;i++)
#define rep1(i,n) for(int i=1;i<=n;i++)
#define per1(i,n) for(int i=n;i>=1;i--)
#define Rep1(i,sta,n) for(int i=sta;i<=n;i++)
#define all(v) (v).begin(),(v).end()
typedef pair<ll, ll> LP;

template<typename T>
void chmin(T& a, T b) {
	a = min(a, b);
}
template<typename T>
void chmax(T& a, T b) {
	a = max(a, b);
}
template<typename T>
void cinarray(vector<T>& v) {
	rep(i, v.size())cin >> v[i];
}
template<typename T>
void coutarray(vector<T>& v) {
	rep(i, v.size()) {
		if (i > 0)cout << " "; cout << v[i];
	}
	cout << "\n";
}
ll mod_pow(ll x, ll n, ll m = mod) {
	if (n < 0) {
		ll res = mod_pow(x, -n, m);
		return mod_pow(res, m - 2, m);
	}
	if (abs(x) >= m)x %= m;
	if (x < 0)x += m;
	//if (x == 0)return 0;
	ll res = 1;
	while (n) {
		if (n & 1)res = res * x % m;
		x = x * x % m; n >>= 1;
	}
	return res;
}
struct modint {
	int n;
	modint() :n(0) { ; }
	modint(ll m) {
		if (m < 0 || mod <= m) {
			m %= mod; if (m < 0)m += mod;
		}
		n = m;
	}
	operator int() { return n; }
};
bool operator==(modint a, modint b) { return a.n == b.n; }
bool operator<(modint a, modint b) { return a.n < b.n; }
modint operator+=(modint& a, modint b) { a.n += b.n; if (a.n >= mod)a.n -= mod; return a; }
modint operator-=(modint& a, modint b) { a.n -= b.n; if (a.n < 0)a.n += mod; return a; }
modint operator*=(modint& a, modint b) { a.n = ((ll)a.n * b.n) % mod; return a; }
modint operator+(modint a, modint b) { return a += b; }
modint operator-(modint a, modint b) { return a -= b; }
modint operator*(modint a, modint b) { return a *= b; }
modint operator^(modint a, ll n) {
	if (n == 0)return modint(1);
	modint res = (a * a) ^ (n / 2);
	if (n % 2)res = res * a;
	return res;
}

ll inv(ll a, ll p) {
	return (a == 1 ? 1 : (1 - p * inv(p % a, a)) / a + p);
}
modint operator/(modint a, modint b) { return a * modint(inv(b, mod)); }
modint operator/=(modint& a, modint b) { a = a / b; return a; }
const int max_n = 1 << 20;
modint fact[max_n], factinv[max_n];
void init_f() {
	fact[0] = modint(1);
	for (int i = 0; i < max_n - 1; i++) {
		fact[i + 1] = fact[i] * modint(i + 1);
	}
	factinv[max_n - 1] = modint(1) / fact[max_n - 1];
	for (int i = max_n - 2; i >= 0; i--) {
		factinv[i] = factinv[i + 1] * modint(i + 1);
	}
}
modint comb(int a, int b) {
	if (a < 0 || b < 0 || a < b)return 0;
	return fact[a] * factinv[b] * factinv[a - b];
}
modint combP(int a, int b) {
	if (a < 0 || b < 0 || a < b)return 0;
	return fact[a] * factinv[a - b];
}

ll gcd(ll a, ll b) {
	a = abs(a); b = abs(b);
	if (a < b)swap(a, b);
	while (b) {
		ll r = a % b; a = b; b = r;
	}
	return a;
}
typedef long double ld;
typedef pair<ld, ld> LDP;
const ld eps = 1e-8;
const ld pi = acosl(-1.0);
template<typename T>
void addv(vector<T>& v, int loc, T val) {
	if (loc >= v.size())v.resize(loc + 1, 0);
	v[loc] += val;
}
/*const int mn = 100005;
bool isp[mn];
vector<int> ps;
void init() {
	fill(isp + 2, isp + mn, true);
	for (int i = 2; i < mn; i++) {
		if (!isp[i])continue;
		ps.push_back(i);
		for (int j = 2 * i; j < mn; j += i) {
			isp[j] = false;
		}
	}
}*/

//[,val)
template<typename T>
auto prev_itr(set<T>& st, T val) {
	auto res = st.lower_bound(val);
	if (res == st.begin())return st.end();
	res--; return res;
}

//[val,)
template<typename T>
auto next_itr(set<T>& st, T val) {
	auto res = st.lower_bound(val);
	return res;
}
using mP = pair<modint, modint>;


int dx[4] = { 1,0,-1,0 };
int dy[4] = { 0,1,0,-1 };
//-----------------------------------------

typedef modint Data;
typedef vector<Data> Array;
typedef vector<Array> mat;

bool is_zero(Data dat) { return abs(dat) < eps; }
mat operator-(mat a) {
	rep(i, a.size())rep(j, a[0].size())a[i][j] = -a[i][j];
	return a;
}
mat operator+(mat lhs, mat& rhs) {
	rep(i, lhs.size())rep(j, lhs[0].size())lhs[i][j] += rhs[i][j];
	return lhs;
}
mat operator-(mat lhs, mat& rhs) {
	rep(i, lhs.size())rep(j, lhs[0].size())lhs[i][j] -= rhs[i][j];
	return lhs;
}
mat operator*(const mat& lhs, const mat& rhs) {
	mat ret(lhs.size(), Array(rhs[0].size(), 0));
	rep(i, lhs.size())rep(j, rhs[0].size())rep(k, rhs.size()) {
		ret[i][j] += lhs[i][k] * rhs[k][j];
	}
	return ret;
}

pair<mat, vector<int>> LUPDecomposition(mat a) {
	int n = a.size();
	vector<int> perm(n);
	iota(begin(perm), end(perm), 0);
	rep(i, n) {
		int pivot = i;
		for (int j = i + 1; j < n; ++j)
			if (abs(a[j][i]) > abs(a[pivot][i]))pivot = j;
		swap(a[pivot], a[i]);
		swap(perm[pivot], perm[i]);
		for (int j = i + 1; j < n; ++j) {
			a[j][i] /= a[i][i];
			for (int k = i + 1; k < n; ++k) {
				a[j][k] -= a[i][k] * a[j][i];
			}
		}
	}
	return make_pair(a, perm);
}
Array LUPBackSubstitution(mat& LU, vector<int>& perm, Array a) {
	int n = LU.size();
	Array tmp(n);
	rep(i, n)tmp[i] = a[perm[i]];
	swap(tmp, a);
	rep(i, n) {
		rep(j, i) {
			a[i] -= a[j] * LU[i][j];
		}
	}
	for (int i = n - 1; i >= 0; --i) {
		for (int j = i + 1; j < n; ++j) {
			a[i] -= a[j] * LU[i][j];
		}
		a[i] /= LU[i][i];
	}
	return a;
}

//Ax=bのxを返す
Array calc(mat A, Array b) {
	pair<mat, vector<int>> p = LUPDecomposition(A);
	return LUPBackSubstitution(p.first, p.second, b);
}


void solve(){
	int n; cin >> n;
	if (n == 2) {
		cout << 2 << "\n"; return;
	}
	vector<P> vp;
	rep(c0, n + 1)rep(c1, n + 1) {
		if (c0 == 0 && c1 == 0)continue;
		vp.push_back({ c0,c1 });
	}
	mat A(vp.size(), Array(vp.size()));
	modint b = n * (n - 1) / 2;
	b = (modint)1 / b;
	Array res(vp.size(), 1);
	rep(i, vp.size()) {
		vector<int> c(3);
		c[0] = vp[i].first;
		c[1] = vp[i].second;
		c[2] = n - c[0] - c[1];
		A[i][i] += 1;
		rep(j, 3) {
			if (c[j] < 2)continue;
			vector<int> nc = c;
			nc[j] -= 2;
			nc[(j + 1)%3] += 2;
			modint prob = c[j] * (c[j] - 1) / 2;
			prob *= b;
			P nex = { nc[0],nc[1] };
			if (nex == P{0, 0})continue;
			int loc = lower_bound(all(vp), nex) - vp.begin();
			A[i][loc] -= prob;
		}
		rep(j, 3)Rep(k, j + 1, 3) {
			if (c[j] < 1 || c[k] < 1)continue;
			vector<int> nc = c;
			nc[j]--; nc[(j + 1) % 3]++;
			nc[k]--; nc[(k + 1) % 3]++;
			modint prob = c[j] * c[k];
			prob *= b;
			P nex = { nc[0],nc[1] };
			if (nex == P{ 0, 0 })continue;
			int loc = lower_bound(all(vp), nex) - vp.begin();
			A[i][loc] -= prob;
		}
	}
	vector<modint> x = calc(A, res);
	int loc = lower_bound(all(vp), P{ n,0 }) - vp.begin();
	cout << x[loc] << "\n";
}

signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	//cout << fixed << setprecision(10);
	//init_f();
	//init();
	//while(true)
	//expr();
	//int t; cin >> t; rep(i, t)
	solve();
	return 0;
}
accuracy: 1
time_ms: 70
memory_kb: 15036
final_rank: 18

code_snippet: #define MOD_TYPE 2

#include <bits/stdc++.h>
using namespace std;

//#include <atcoder/all>


#include <utility>

namespace atcoder {

namespace internal {

// @param m `1 <= m`
// @return x mod m
constexpr long long safe_mod(long long x, long long m) {
    x %= m;
    if (x < 0) x += m;
    return x;
}

// Fast modular multiplication by barrett reduction
// Reference: https://en.wikipedia.org/wiki/Barrett_reduction
// NOTE: reconsider after Ice Lake
struct barrett {
    unsigned int _m;
    unsigned long long im;

    // @param m `1 <= m < 2^31`
    barrett(unsigned int m) : _m(m), im((unsigned long long)(-1) / m + 1) {}

    // @return m
    unsigned int umod() const { return _m; }

    // @param a `0 <= a < m`
    // @param b `0 <= b < m`
    // @return `a * b % m`
    unsigned int mul(unsigned int a, unsigned int b) const {
        // [1] m = 1
        // a = b = im = 0, so okay

        // [2] m >= 2
        // im = ceil(2^64 / m)
        // -> im * m = 2^64 + r (0 <= r < m)
        // let z = a*b = c*m + d (0 <= c, d < m)
        // a*b * im = (c*m + d) * im = c*(im*m) + d*im = c*2^64 + c*r + d*im
        // c*r + d*im < m * m + m * im < m * m + 2^64 + m <= 2^64 + m * (m + 1) < 2^64 * 2
        // ((ab * im) >> 64) == c or c + 1
        unsigned long long z = a;
        z *= b;
#ifdef _MSC_VER
        unsigned long long x;
        _umul128(z, im, &x);
#else
        unsigned long long x =
            (unsigned long long)(((unsigned __int128)(z)*im) >> 64);
#endif
        unsigned int v = (unsigned int)(z - x * _m);
        if (_m <= v) v += _m;
        return v;
    }
};

// @param n `0 <= n`
// @param m `1 <= m`
// @return `(x ** n) % m`
constexpr long long pow_mod_constexpr(long long x, long long n, int m) {
    if (m == 1) return 0;
    unsigned int _m = (unsigned int)(m);
    unsigned long long r = 1;
    unsigned long long y = safe_mod(x, m);
    while (n) {
        if (n & 1) r = (r * y) % _m;
        y = (y * y) % _m;
        n >>= 1;
    }
    return r;
}

// Reference:
// M. Forisek and J. Jancina,
// Fast Primality Testing for Integers That Fit into a Machine Word
// @param n `0 <= n`
constexpr bool is_prime_constexpr(int n) {
    if (n <= 1) return false;
    if (n == 2 || n == 7 || n == 61) return true;
    if (n % 2 == 0) return false;
    long long d = n - 1;
    while (d % 2 == 0) d /= 2;
    constexpr long long bases[3] = {2, 7, 61};
    for (long long a : bases) {
        long long t = d;
        long long y = pow_mod_constexpr(a, t, n);
        while (t != n - 1 && y != 1 && y != n - 1) {
            y = y * y % n;
            t <<= 1;
        }
        if (y != n - 1 && t % 2 == 0) {
            return false;
        }
    }
    return true;
}
template <int n> constexpr bool is_prime = is_prime_constexpr(n);

// @param b `1 <= b`
// @return pair(g, x) s.t. g = gcd(a, b), xa = g (mod b), 0 <= x < b/g
constexpr std::pair<long long, long long> inv_gcd(long long a, long long b) {
    a = safe_mod(a, b);
    if (a == 0) return {b, 0};

    // Contracts:
    // [1] s - m0 * a = 0 (mod b)
    // [2] t - m1 * a = 0 (mod b)
    // [3] s * |m1| + t * |m0| <= b
    long long s = b, t = a;
    long long m0 = 0, m1 = 1;

    while (t) {
        long long u = s / t;
        s -= t * u;
        m0 -= m1 * u;  // |m1 * u| <= |m1| * s <= b

        // [3]:
        // (s - t * u) * |m1| + t * |m0 - m1 * u|
        // <= s * |m1| - t * u * |m1| + t * (|m0| + |m1| * u)
        // = s * |m1| + t * |m0| <= b

        auto tmp = s;
        s = t;
        t = tmp;
        tmp = m0;
        m0 = m1;
        m1 = tmp;
    }
    // by [3]: |m0| <= b/g
    // by g != b: |m0| < b/g
    if (m0 < 0) m0 += b / s;
    return {s, m0};
}

// Compile time primitive root
// @param m must be prime
// @return primitive root (and minimum in now)
constexpr int primitive_root_constexpr(int m) {
    if (m == 2) return 1;
    if (m == 167772161) return 3;
    if (m == 469762049) return 3;
    if (m == 754974721) return 11;
    if (m == 998244353) return 3;
    int divs[20] = {};
    divs[0] = 2;
    int cnt = 1;
    int x = (m - 1) / 2;
    while (x % 2 == 0) x /= 2;
    for (int i = 3; (long long)(i)*i <= x; i += 2) {
        if (x % i == 0) {
            divs[cnt++] = i;
            while (x % i == 0) {
                x /= i;
            }
        }
    }
    if (x > 1) {
        divs[cnt++] = x;
    }
    for (int g = 2;; g++) {
        bool ok = true;
        for (int i = 0; i < cnt; i++) {
            if (pow_mod_constexpr(g, (m - 1) / divs[i], m) == 1) {
                ok = false;
                break;
            }
        }
        if (ok) return g;
    }
}
template <int m> constexpr int primitive_root = primitive_root_constexpr(m);

}  // namespace internal

}  // namespace atcoder


#include <cassert>
#include <numeric>
#include <type_traits>

namespace atcoder {

namespace internal {

#ifndef _MSC_VER
template <class T>
using is_signed_int128 =
    typename std::conditional<std::is_same<T, __int128_t>::value ||
                                  std::is_same<T, __int128>::value,
                              std::true_type,
                              std::false_type>::type;

template <class T>
using is_unsigned_int128 =
    typename std::conditional<std::is_same<T, __uint128_t>::value ||
                                  std::is_same<T, unsigned __int128>::value,
                              std::true_type,
                              std::false_type>::type;

template <class T>
using make_unsigned_int128 =
    typename std::conditional<std::is_same<T, __int128_t>::value,
                              __uint128_t,
                              unsigned __int128>;

template <class T>
using is_integral = typename std::conditional<std::is_integral<T>::value ||
                                                  is_signed_int128<T>::value ||
                                                  is_unsigned_int128<T>::value,
                                              std::true_type,
                                              std::false_type>::type;

template <class T>
using is_signed_int = typename std::conditional<(is_integral<T>::value &&
                                                 std::is_signed<T>::value) ||
                                                    is_signed_int128<T>::value,
                                                std::true_type,
                                                std::false_type>::type;

template <class T>
using is_unsigned_int =
    typename std::conditional<(is_integral<T>::value &&
                               std::is_unsigned<T>::value) ||
                                  is_unsigned_int128<T>::value,
                              std::true_type,
                              std::false_type>::type;

template <class T>
using to_unsigned = typename std::conditional<
    is_signed_int128<T>::value,
    make_unsigned_int128<T>,
    typename std::conditional<std::is_signed<T>::value,
                              std::make_unsigned<T>,
                              std::common_type<T>>::type>::type;

#else

template <class T> using is_integral = typename std::is_integral<T>;

template <class T>
using is_signed_int =
    typename std::conditional<is_integral<T>::value && std::is_signed<T>::value,
                              std::true_type,
                              std::false_type>::type;

template <class T>
using is_unsigned_int =
    typename std::conditional<is_integral<T>::value &&
                                  std::is_unsigned<T>::value,
                              std::true_type,
                              std::false_type>::type;

template <class T>
using to_unsigned = typename std::conditional<is_signed_int<T>::value,
                                              std::make_unsigned<T>,
                                              std::common_type<T>>::type;

#endif

template <class T>
using is_signed_int_t = std::enable_if_t<is_signed_int<T>::value>;

template <class T>
using is_unsigned_int_t = std::enable_if_t<is_unsigned_int<T>::value>;

template <class T> using to_unsigned_t = typename to_unsigned<T>::type;

}  // namespace internal

}  // namespace atcoder

#include <cassert>
#include <numeric>
#include <type_traits>

#ifdef _MSC_VER
#include <intrin.h>
#endif

namespace atcoder {

namespace internal {

struct modint_base {};
struct static_modint_base : modint_base {};

template <class T> using is_modint = std::is_base_of<modint_base, T>;
template <class T> using is_modint_t = std::enable_if_t<is_modint<T>::value>;

}  // namespace internal

template <int m, std::enable_if_t<(1 <= m)>* = nullptr>
struct static_modint : internal::static_modint_base {
    using mint = static_modint;

  public:
    static constexpr int mod() { return m; }
    static mint raw(int v) {
        mint x;
        x._v = v;
        return x;
    }

    static_modint() : _v(0) {}
    template <class T, internal::is_signed_int_t<T>* = nullptr>
    static_modint(T v) {
        long long x = (long long)(v % (long long)(umod()));
        if (x < 0) x += umod();
        _v = (unsigned int)(x);
    }
    template <class T, internal::is_unsigned_int_t<T>* = nullptr>
    static_modint(T v) {
        _v = (unsigned int)(v % umod());
    }
    static_modint(bool v) { _v = ((unsigned int)(v) % umod()); }

    unsigned int val() const { return _v; }

    mint& operator++() {
        _v++;
        if (_v == umod()) _v = 0;
        return *this;
    }
    mint& operator--() {
        if (_v == 0) _v = umod();
        _v--;
        return *this;
    }
    mint operator++(int) {
        mint result = *this;
        ++*this;
        return result;
    }
    mint operator--(int) {
        mint result = *this;
        --*this;
        return result;
    }

    mint& operator+=(const mint& rhs) {
        _v += rhs._v;
        if (_v >= umod()) _v -= umod();
        return *this;
    }
    mint& operator-=(const mint& rhs) {
        _v -= rhs._v;
        if (_v >= umod()) _v += umod();
        return *this;
    }
    mint& operator*=(const mint& rhs) {
        unsigned long long z = _v;
        z *= rhs._v;
        _v = (unsigned int)(z % umod());
        return *this;
    }
    mint& operator/=(const mint& rhs) { return *this = *this * rhs.inv(); }

    mint operator+() const { return *this; }
    mint operator-() const { return mint() - *this; }

    mint pow(long long n) const {
        assert(0 <= n);
        mint x = *this, r = 1;
        while (n) {
            if (n & 1) r *= x;
            x *= x;
            n >>= 1;
        }
        return r;
    }
    mint inv() const {
        if (prime) {
            assert(_v);
            return pow(umod() - 2);
        } else {
            auto eg = internal::inv_gcd(_v, m);
            assert(eg.first == 1);
            return eg.second;
        }
    }

    friend mint operator+(const mint& lhs, const mint& rhs) {
        return mint(lhs) += rhs;
    }
    friend mint operator-(const mint& lhs, const mint& rhs) {
        return mint(lhs) -= rhs;
    }
    friend mint operator*(const mint& lhs, const mint& rhs) {
        return mint(lhs) *= rhs;
    }
    friend mint operator/(const mint& lhs, const mint& rhs) {
        return mint(lhs) /= rhs;
    }
    friend bool operator==(const mint& lhs, const mint& rhs) {
        return lhs._v == rhs._v;
    }
    friend bool operator!=(const mint& lhs, const mint& rhs) {
        return lhs._v != rhs._v;
    }

  private:
    unsigned int _v;
    static constexpr unsigned int umod() { return m; }
    static constexpr bool prime = internal::is_prime<m>;
};

template <int id> struct dynamic_modint : internal::modint_base {
    using mint = dynamic_modint;

  public:
    static int mod() { return (int)(bt.umod()); }
    static void set_mod(int m) {
        assert(1 <= m);
        bt = internal::barrett(m);
    }
    static mint raw(int v) {
        mint x;
        x._v = v;
        return x;
    }

    dynamic_modint() : _v(0) {}
    template <class T, internal::is_signed_int_t<T>* = nullptr>
    dynamic_modint(T v) {
        long long x = (long long)(v % (long long)(mod()));
        if (x < 0) x += mod();
        _v = (unsigned int)(x);
    }
    template <class T, internal::is_unsigned_int_t<T>* = nullptr>
    dynamic_modint(T v) {
        _v = (unsigned int)(v % mod());
    }
    dynamic_modint(bool v) { _v = ((unsigned int)(v) % mod()); }

    unsigned int val() const { return _v; }

    mint& operator++() {
        _v++;
        if (_v == umod()) _v = 0;
        return *this;
    }
    mint& operator--() {
        if (_v == 0) _v = umod();
        _v--;
        return *this;
    }
    mint operator++(int) {
        mint result = *this;
        ++*this;
        return result;
    }
    mint operator--(int) {
        mint result = *this;
        --*this;
        return result;
    }

    mint& operator+=(const mint& rhs) {
        _v += rhs._v;
        if (_v >= umod()) _v -= umod();
        return *this;
    }
    mint& operator-=(const mint& rhs) {
        _v += mod() - rhs._v;
        if (_v >= umod()) _v -= umod();
        return *this;
    }
    mint& operator*=(const mint& rhs) {
        _v = bt.mul(_v, rhs._v);
        return *this;
    }
    mint& operator/=(const mint& rhs) { return *this = *this * rhs.inv(); }

    mint operator+() const { return *this; }
    mint operator-() const { return mint() - *this; }

    mint pow(long long n) const {
        assert(0 <= n);
        mint x = *this, r = 1;
        while (n) {
            if (n & 1) r *= x;
            x *= x;
            n >>= 1;
        }
        return r;
    }
    mint inv() const {
        auto eg = internal::inv_gcd(_v, mod());
        assert(eg.first == 1);
        return eg.second;
    }

    friend mint operator+(const mint& lhs, const mint& rhs) {
        return mint(lhs) += rhs;
    }
    friend mint operator-(const mint& lhs, const mint& rhs) {
        return mint(lhs) -= rhs;
    }
    friend mint operator*(const mint& lhs, const mint& rhs) {
        return mint(lhs) *= rhs;
    }
    friend mint operator/(const mint& lhs, const mint& rhs) {
        return mint(lhs) /= rhs;
    }
    friend bool operator==(const mint& lhs, const mint& rhs) {
        return lhs._v == rhs._v;
    }
    friend bool operator!=(const mint& lhs, const mint& rhs) {
        return lhs._v != rhs._v;
    }

  private:
    unsigned int _v;
    static internal::barrett bt;
    static unsigned int umod() { return bt.umod(); }
};
template <int id> internal::barrett dynamic_modint<id>::bt = 998244353;

using modint998244353 = static_modint<998244353>;
using modint1000000007 = static_modint<1000000007>;
using modint = dynamic_modint<-1>;

namespace internal {

template <class T>
using is_static_modint = std::is_base_of<internal::static_modint_base, T>;

template <class T>
using is_static_modint_t = std::enable_if_t<is_static_modint<T>::value>;

template <class> struct is_dynamic_modint : public std::false_type {};
template <int id>
struct is_dynamic_modint<dynamic_modint<id>> : public std::true_type {};

template <class T>
using is_dynamic_modint_t = std::enable_if_t<is_dynamic_modint<T>::value>;

}  // namespace internal

}  // namespace atcoder

//#include <atcoder/lazysegtree>
//#include <atcoder/segtree>

using namespace atcoder;

#if 0
#include <boost/multiprecision/cpp_dec_float.hpp>
#include <boost/multiprecision/cpp_int.hpp>
using Int = boost::multiprecision::cpp_int;
using lld = boost::multiprecision::cpp_dec_float_100;
#endif

#if 0
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tag_and_trait.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/rope>
using namespace __gnu_pbds;
using namespace __gnu_cxx;
template <typename T>
using extset =
    tree<T, null_type, less<T>, rb_tree_tag, tree_order_statistics_node_update>;
#endif

#if 1
#pragma GCC target("avx2")
#pragma GCC optimize("O3")
#pragma GCC optimize("unroll-loops")
#endif

#pragma region Macros

using ll = long long int;
using ld = long double;
using pii = pair<int, int>;
using pll = pair<ll, ll>;
using pld = pair<ld, ld>;
template <typename Q_type>
using smaller_queue = priority_queue<Q_type, vector<Q_type>, greater<Q_type>>;

#if MOD_TYPE == 1
constexpr ll MOD = ll(1e9 + 7);
#else
#if MOD_TYPE == 2
constexpr ll MOD = 998244353;
#else
constexpr ll MOD = 1000003;
#endif
#endif

using mint = static_modint<MOD>;
constexpr int INF = (int)1e9 + 10;
constexpr ll LINF = (ll)4e18;
constexpr double PI = acos(-1.0);
constexpr double EPS = 1e-11;
constexpr int Dx[] = {0, 0, -1, 1, -1, 1, -1, 1, 0};
constexpr int Dy[] = {1, -1, 0, 0, -1, -1, 1, 1, 0};

#define REP(i, m, n) for (ll i = m; i < (ll)(n); ++i)
#define rep(i, n) REP(i, 0, n)
#define REPI(i, m, n) for (int i = m; i < (int)(n); ++i)
#define repi(i, n) REPI(i, 0, n)
#define YES(n) cout << ((n) ? "YES" : "NO") << "\n"
#define Yes(n) cout << ((n) ? "Yes" : "No") << "\n"
#define possible(n) cout << ((n) ? "possible" : "impossible") << "\n"
#define Possible(n) cout << ((n) ? "Possible" : "Impossible") << "\n"
#define all(v) v.begin(), v.end()
#define NP(v) next_permutation(all(v))
#define dbg(x) cerr << #x << ":" << x << "\n";
#define UNIQUE(v) v.erase(unique(all(v)), v.end())

struct io_init {
  io_init() {
    cin.tie(nullptr);
    ios::sync_with_stdio(false);
    cout << setprecision(30) << setiosflags(ios::fixed);
  };
} io_init;
template <typename T>
inline bool chmin(T &a, T b) {
  if (a > b) {
    a = b;
    return true;
  }
  return false;
}
template <typename T>
inline bool chmax(T &a, T b) {
  if (a < b) {
    a = b;
    return true;
  }
  return false;
}
inline ll CEIL(ll a, ll b) { return (a + b - 1) / b; }
template <typename A, size_t N, typename T>
inline void Fill(A (&array)[N], const T &val) {
  fill((T *)array, (T *)(array + N), val);
}
template <typename T>
vector<T> compress(vector<T> &v) {
  vector<T> val = v;
  sort(all(val)), val.erase(unique(all(val)), val.end());
  for (auto &&vi : v) vi = lower_bound(all(val), vi) - val.begin();
  return val;
}
template <typename T, typename U>
constexpr istream &operator>>(istream &is, pair<T, U> &p) noexcept {
  is >> p.first >> p.second;
  return is;
}
template <typename T, typename U>
constexpr ostream &operator<<(ostream &os, pair<T, U> p) noexcept {
  os << p.first << " " << p.second;
  return os;
}
ostream &operator<<(ostream &os, mint m) {
  os << m.val();
  return os;
}
ostream &operator<<(ostream &os, modint m) {
  os << m.val();
  return os;
}
template <typename T>
constexpr istream &operator>>(istream &is, vector<T> &v) noexcept {
  for (int i = 0; i < v.size(); i++) is >> v[i];
  return is;
}
template <typename T>
constexpr ostream &operator<<(ostream &os, vector<T> &v) noexcept {
  for (int i = 0; i < v.size(); i++)
    os << v[i] << (i + 1 == v.size() ? "" : " ");
  return os;
}
template <typename T>
constexpr void operator--(vector<T> &v, int) noexcept {
  for (int i = 0; i < v.size(); i++) v[i]--;
}

random_device seed_gen;
mt19937_64 engine(seed_gen());

struct BiCoef {
  vector<mint> fact_, inv_, finv_;
  BiCoef(int n) noexcept : fact_(n, 1), inv_(n, 1), finv_(n, 1) {
    fact_.assign(n, 1), inv_.assign(n, 1), finv_.assign(n, 1);
    for (int i = 2; i < n; i++) {
      fact_[i] = fact_[i - 1] * i;
      inv_[i] = -inv_[MOD % i] * (MOD / i);
      finv_[i] = finv_[i - 1] * inv_[i];
    }
  }
  mint C(ll n, ll k) const noexcept {
    if (n < k || n < 0 || k < 0) return 0;
    return fact_[n] * finv_[k] * finv_[n - k];
  }
  mint P(ll n, ll k) const noexcept { return C(n, k) * fact_[k]; }
  mint H(ll n, ll k) const noexcept { return C(n + k - 1, k); }
  mint Ch1(ll n, ll k) const noexcept {
    if (n < 0 || k < 0) return 0;
    mint res = 0;
    for (int i = 0; i < n; i++)
      res += C(n, i) * mint(n - i).pow(k) * (i & 1 ? -1 : 1);
    return res;
  }
  mint fact(ll n) const noexcept {
    if (n < 0) return 0;
    return fact_[n];
  }
  mint inv(ll n) const noexcept {
    if (n < 0) return 0;
    return inv_[n];
  }
  mint finv(ll n) const noexcept {
    if (n < 0) return 0;
    return finv_[n];
  }
};

BiCoef bc(200010);

#pragma endregion

// -------------------------------

long long modinv(long long a, long long mod) {
  long long b = mod, u = 1, v = 0;
  while (b) {
    long long t = a / b;
    a -= t * b;
    swap(a, b);
    u -= t * v;
    swap(u, v);
  }
  u %= mod;
  if (u < 0) u += mod;
  return u;
}

// matrix
template <int MOD>
struct Matrix {
  vector<vector<long long>> val;
  Matrix(int n, int m, long long x = 0) : val(n, vector<long long>(m, x)) {}
  void init(int n, int m, long long x = 0) {
    val.assign(n, vector<long long>(m, x));
  }
  size_t size() const { return val.size(); }
  inline vector<long long> &operator[](int i) { return val[i]; }
};

template <int MOD>
ostream &operator<<(ostream &s, Matrix<MOD> A) {
  s << endl;
  for (int i = 0; i < A.size(); ++i) {
    for (int j = 0; j < A[i].size(); ++j) {
      s << A[i][j] << ", ";
    }
    s << endl;
  }
  return s;
}

template <int MOD>
Matrix<MOD> operator*(Matrix<MOD> A, Matrix<MOD> B) {
  Matrix<MOD> R(A.size(), B[0].size());
  for (int i = 0; i < A.size(); ++i)
    for (int j = 0; j < B[0].size(); ++j)
      for (int k = 0; k < B.size(); ++k)
        R[i][j] = (R[i][j] + A[i][k] * B[k][j] % MOD) % MOD;
  return R;
}

template <int MOD>
Matrix<MOD> pow(Matrix<MOD> A, long long n) {
  Matrix<MOD> R(A.size(), A.size());
  for (int i = 0; i < A.size(); ++i) R[i][i] = 1;
  while (n > 0) {
    if (n & 1) R = R * A;
    A = A * A;
    n >>= 1;
  }
  return R;
}

template <int MOD>
int GaussJordan(Matrix<MOD> &A, bool is_extended = false) {
  int m = A.size(), n = A[0].size();
  for (int row = 0; row < m; ++row)
    for (int col = 0; col < n; ++col)
      A[row][col] = (A[row][col] % MOD + MOD) % MOD;
  int rank = 0;
  for (int col = 0; col < n; ++col) {
    if (is_extended && col == n - 1) break;
    int pivot = -1;
    for (int row = rank; row < m; ++row) {
      if (A[row][col] != 0) {
        pivot = row;
        break;
      }
    }
    if (pivot == -1) continue;
    swap(A[pivot], A[rank]);
    auto inv = modinv(A[rank][col], MOD);
    for (int col2 = 0; col2 < n; ++col2)
      A[rank][col2] = A[rank][col2] * inv % MOD;
    for (int row = 0; row < m; ++row) {
      if (row != rank && A[row][col]) {
        auto fac = A[row][col];
        for (int col2 = 0; col2 < n; ++col2) {
          A[row][col2] -= A[rank][col2] * fac % MOD;
          if (A[row][col2] < 0) A[row][col2] += MOD;
        }
      }
    }
    ++rank;
  }
  return rank;
}

template <int MOD>
int linear_equation(Matrix<MOD> A, vector<long long> b,
                    vector<long long> &res) {
  int m = A.size(), n = A[0].size();
  Matrix<MOD> M(m, n + 1);
  for (int i = 0; i < m; ++i) {
    for (int j = 0; j < n; ++j) M[i][j] = A[i][j];
    M[i][n] = b[i];
  }
  int rank = GaussJordan(M, true);

  // check if it has no solution
  for (int row = rank; row < m; ++row)
    if (M[row][n]) return -1;

  // answer
  res.assign(n, 0);
  for (int i = 0; i < rank; ++i) res[i] = M[i][n];
  return rank;
}

void solve() {
  int n;
  cin >> n;
  if (n == 2) {
    cout << 2 << "\n";
    return;
  }
  map<array<int, 3>, int> mp;
  repi(i, n + 1) repi(j, n + 1) {
    int k = n - i - j;
    if (k < 0) continue;
    mp[{i, j, k}] = 0;
  }
  int z = 0;
  for (auto &&[k, v] : mp) v = z++;

  Matrix<MOD> A(z, z);
  vector<ll> b(z, MOD - 1);
  mint bunbo = (bc.inv(2) * n * (n - 1)).inv();
  auto add = [&](int ai, int i, mint k) {
    A[ai][i] += (k * bunbo).val();
    if (A[ai][i] >= MOD) A[ai][i] -= MOD;
  };

  repi(i, n + 1) repi(j, n + 1) {
    int k = n - i - j;
    if (k < 0) continue;
    int ai = mp[{i, j, k}];
    if (i == 0 and j == 0 and k == n) {
      A[ai][ai] = 1;
      b[ai] = 0;
    } else {
      A[ai][mp[{i, j, k}]] = MOD - 1;
      // 0,0
      if (i >= 2) add(ai, mp[{i - 2, j + 2, k}], i * (i - 1) / 2);
      // 1,1
      if (j >= 2) add(ai, mp[{i, j - 2, k + 2}], j * (j - 1) / 2);
      // 2,2
      if (k >= 2) add(ai, mp[{i + 2, j, k - 2}], k * (k - 1) / 2);
      // 0,1
      if (i >= 1 and j >= 1) add(ai, mp[{i - 1, j, k + 1}], i * j);
      // 1,2
      if (j >= 1 and k >= 1) add(ai, mp[{i + 1, j - 1, k}], j * k);
      // 2,0
      if (k >= 1 and i >= 1) add(ai, mp[{i, j + 1, k - 1}], k * i);
    }
  }
  /*
    rep(i, z) {
      rep(j, z) cerr << A[i][j] << " ";
      cerr << "\n";
    }
    cerr << b << "\n";*/

  vector<ll> res;
  int r = linear_equation(A, b, res);
  cout << res[mp[{n, 0, 0}]] << "\n";
}

int main() { solve(); }
accuracy: 1
time_ms: 10
memory_kb: 7028
final_rank: 10

code_snippet: #include <bits/stdc++.h>
#pragma GCC optimize("Ofast")
#define _GLIBCXX_DEBUG
using namespace std;
using std::cout;
using std::cin;
using std::endl;
using ll=long long;
using ld=long double;
ll ILL=1167167167167167167;
const int INF=2100000000;
const ll mod=998244353;
#define rep(i,a) for (ll i=0;i<a;i++)
#define all(p) p.begin(),p.end()
template<class T> using _pq = priority_queue<T, vector<T>, greater<T>>;
template<class T> ll LB(vector<T> &v,T a){return lower_bound(v.begin(),v.end(),a)-v.begin();}
template<class T> ll UB(vector<T> &v,T a){return upper_bound(v.begin(),v.end(),a)-v.begin();}
template<class T> bool chmin(T &a,const T &b){if(a>b){a=b;return 1;}else return 0;}
template<class T> bool chmax(T &a,const T &b){if(a<b){a=b;return 1;}else return 0;}
template<class T> void So(vector<T> &v) {sort(v.begin(),v.end());}
template<class T> void Sore(vector<T> &v) {sort(v.begin(),v.end(),[](T x,T y){return x>y;});}
void yneos(bool a){if(a) cout<<"Yes\n"; else cout<<"No\n";}
template<class T> void vec_out(vector<T> &p){for(int i=0;i<(int)(p.size());i++){if(i) cout<<" ";cout<<p[i];}cout<<"\n";}

ll jyo(ll x,ll y,ll z){
  ll H=y; //ここから
       ll a=1,b=(x%z+z)%z,c=1;
       while(H>0){
         a*=2;
         if(H%a!=0){
           H-=a/2;
           c*=b;
           c%=z;
         }
        b*=b;
         b%=z;
      } //ここまで
 return c;
}


void solve();
// oddloop
int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	
	int t=1;
	//cin>>t;
	rep(i,t) solve();
}

void solve(){
	int N;
	cin>>N;
	map<tuple<int,int,int>,int> m;
	vector<tuple<int,int,int>> order;
	int ind=0;
	rep(i,N+1) rep(j,N+1){
		int a=i,b=j;
		int c=N-a-b; 
		if(c<0) continue;
		m[{a,b,c}]=ind;
		order.push_back({a,b,c});
		ind++;
	}
	vector<vector<ll>> base(ind,vector<ll>(ind+1));
	ll R=jyo(N*(N-1),mod-2,mod);
	rep(i,ind){
		ll a,b,c;
		tie(a,b,c)=order[i];
		if(c==N){
			base[0][0]=1;
			continue;
		}
		base[i][ind]=-1;
		base[i][i]=-1;
		base[i][m[{a-2,b+2,c}]]+=(R*a*(a-1))%mod;
		base[i][m[{a,b-2,c+2}]]+=(R*b*(b-1))%mod;
		base[i][m[{a+2,b,c-2}]]+=(R*c*(c-1))%mod;
		base[i][m[{a-1,b,c+1}]]+=(R*a*b*2)%mod;
		base[i][m[{a+1,b-1,c}]]+=(R*b*c*2)%mod;
		base[i][m[{a,b+1,c-1}]]+=(R*a*c*2)%mod;
	}
	rep(i,ind){
		rep(j,ind+1) base[i][j]%=mod;//,cout<<base[i][j]<<" ";
		//cout<<endl;
	}
	rep(i,ind){
		for(int k=i;k<ind;k++){
			base[k][i]=(base[k][i]%mod+mod)%mod;
			if(base[k][i]!=0){
				swap(base[i],base[k]);
				swap(order[k],order[i]);
				m[{order[k]}]=k;
				m[{order[i]}]=i;
				break;
			}
		}
		ll r=jyo(base[i][i],mod-2,mod);
		rep(j,ind+1){
			base[i][j]=(base[i][j]*r)%mod;
		}
		rep(j,ind){
			if(i==j) continue;
			ll B=base[j][i]%mod;
			rep(k,ind+1){
				base[j][k]=(base[j][k]-(base[i][k]*B)%mod)%mod;
			}
		}
	}
	/*rep(i,ind){
		rep(j,ind+1) cout<<base[i][j]<<" ";
		cout<<endl;
	}*/
	int x=m[{N,0,0}];
	ll ans=base[x][ind];
	cout<<(ans%mod+mod)%mod<<"\n";
}
accuracy: 1
time_ms: 20
memory_kb: 3836
final_rank: 8

code_snippet: /**
 *  date : 2022-03-19 13:38:56
 */

#define NDEBUG
using namespace std;

// intrinstic
#include <immintrin.h>

#include <algorithm>
#include <array>
#include <bitset>
#include <cassert>
#include <cctype>
#include <cfenv>
#include <cfloat>
#include <chrono>
#include <cinttypes>
#include <climits>
#include <cmath>
#include <complex>
#include <cstdarg>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <deque>
#include <fstream>
#include <functional>
#include <initializer_list>
#include <iomanip>
#include <ios>
#include <iostream>
#include <istream>
#include <iterator>
#include <limits>
#include <list>
#include <map>
#include <memory>
#include <new>
#include <numeric>
#include <ostream>
#include <queue>
#include <random>
#include <set>
#include <sstream>
#include <stack>
#include <streambuf>
#include <string>
#include <tuple>
#include <type_traits>
#include <typeinfo>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>

// utility
namespace Nyaan {
using ll = long long;
using i64 = long long;
using u64 = unsigned long long;
using i128 = __int128_t;
using u128 = __uint128_t;

template <typename T>
using V = vector<T>;
template <typename T>
using VV = vector<vector<T>>;
using vi = vector<int>;
using vl = vector<long long>;
using vd = V<double>;
using vs = V<string>;
using vvi = vector<vector<int>>;
using vvl = vector<vector<long long>>;

template <typename T, typename U>
struct P : pair<T, U> {
  template <typename... Args>
  P(Args... args) : pair<T, U>(args...) {}

  using pair<T, U>::first;
  using pair<T, U>::second;

  P &operator+=(const P &r) {
    first += r.first;
    second += r.second;
    return *this;
  }
  P &operator-=(const P &r) {
    first -= r.first;
    second -= r.second;
    return *this;
  }
  P &operator*=(const P &r) {
    first *= r.first;
    second *= r.second;
    return *this;
  }
  template <typename S>
  P &operator*=(const S &r) {
    first *= r, second *= r;
    return *this;
  }
  P operator+(const P &r) const { return P(*this) += r; }
  P operator-(const P &r) const { return P(*this) -= r; }
  P operator*(const P &r) const { return P(*this) *= r; }
  template <typename S>
  P operator*(const S &r) const {
    return P(*this) *= r;
  }
  P operator-() const { return P{-first, -second}; }
};

using pl = P<ll, ll>;
using pi = P<int, int>;
using vp = V<pl>;

constexpr int inf = 1001001001;
constexpr long long infLL = 4004004004004004004LL;

template <typename T>
int sz(const T &t) {
  return t.size();
}

template <typename T, typename U>
inline bool amin(T &x, U y) {
  return (y < x) ? (x = y, true) : false;
}
template <typename T, typename U>
inline bool amax(T &x, U y) {
  return (x < y) ? (x = y, true) : false;
}

template <typename T>
inline T Max(const vector<T> &v) {
  return *max_element(begin(v), end(v));
}
template <typename T>
inline T Min(const vector<T> &v) {
  return *min_element(begin(v), end(v));
}
template <typename T>
inline long long Sum(const vector<T> &v) {
  return accumulate(begin(v), end(v), 0LL);
}

template <typename T>
int lb(const vector<T> &v, const T &a) {
  return lower_bound(begin(v), end(v), a) - begin(v);
}
template <typename T>
int ub(const vector<T> &v, const T &a) {
  return upper_bound(begin(v), end(v), a) - begin(v);
}

constexpr long long TEN(int n) {
  long long ret = 1, x = 10;
  for (; n; x *= x, n >>= 1) ret *= (n & 1 ? x : 1);
  return ret;
}

template <typename T, typename U>
pair<T, U> mkp(const T &t, const U &u) {
  return make_pair(t, u);
}

template <typename T>
vector<T> mkrui(const vector<T> &v, bool rev = false) {
  vector<T> ret(v.size() + 1);
  if (rev) {
    for (int i = int(v.size()) - 1; i >= 0; i--) ret[i] = v[i] + ret[i + 1];
  } else {
    for (int i = 0; i < int(v.size()); i++) ret[i + 1] = ret[i] + v[i];
  }
  return ret;
};

template <typename T>
vector<T> mkuni(const vector<T> &v) {
  vector<T> ret(v);
  sort(ret.begin(), ret.end());
  ret.erase(unique(ret.begin(), ret.end()), ret.end());
  return ret;
}

template <typename F>
vector<int> mkord(int N,F f) {
  vector<int> ord(N);
  iota(begin(ord), end(ord), 0);
  sort(begin(ord), end(ord), f);
  return ord;
}

template <typename T>
vector<int> mkinv(vector<T> &v) {
  int max_val = *max_element(begin(v), end(v));
  vector<int> inv(max_val + 1, -1);
  for (int i = 0; i < (int)v.size(); i++) inv[v[i]] = i;
  return inv;
}

vector<int> mkiota(int n) {
  vector<int> ret(n);
  iota(begin(ret), end(ret), 0);
  return ret;
}

template <typename T>
T mkrev(const T &v) {
  T w{v};
  reverse(begin(w), end(w));
  return w;
}

template <typename T>
bool nxp(vector<T> &v) {
  return next_permutation(begin(v), end(v));
}



#define inV(T, v, n) \
  vector<T> v(n);    \
  in(v)

#define inVV(T, v, h, w)                \
  vector<vector<T>> v(h, vector<T>(w)); \
  in(v);

template <typename T>
using minpq = priority_queue<T, vector<T>, greater<T>>;

// 区間：半開区間 (ng, ok] または [ok, ng)
template <typename T, typename F>
T binary_search(T ng, T ok, const F& f) {
  if constexpr (is_integral<T>::value == true) {
    while (abs(ok - ng) > 1) {
      T x = (ok + ng) / 2;
      (f(x) ? ok : ng) = x;
    }
    return ok;
  } else {
    for (int iter = 0; iter < 60; iter++) {
      T x = (ok + ng) / 2;
      (f(x) ? ok : ng) = x;
    }
    return ok;
  }
}

// 解区間 (l, r)
template <typename T, typename F>
void ternary_search(T l, T r, const F& f, bool greater = false) {
  if constexpr (is_integral<T>::value == true) {
    while (abs(l - r) > 2) {
      T llr = (l * 2 + r * 1) / 3;
      T lrr = (l * 1 + r * 2) / 3;
      bool flag = f(llr) < f(lrr);
      if (flag != greater) {
        r = lrr;
      } else {
        l = llr;
      }
    }
    return (l + r) / 2;
  } else {
    for (int iter = 0; iter < 80; iter++) {
      T llr = (l * 2 + r * 1) / 3;
      T lrr = (l * 1 + r * 2) / 3;
      bool flag = f(llr) < f(lrr);
      if (flag != greater) {
        r = lrr;
      } else {
        l = llr;
      }
    }
    return (l + r) / 2;
  }
}

}  // namespace Nyaan

// bit operation
namespace Nyaan {
__attribute__((target("popcnt"))) inline int popcnt(const u64 &a) {
  return _mm_popcnt_u64(a);
}
inline int lsb(const u64 &a) { return a ? __builtin_ctzll(a) : 64; }
inline int ctz(const u64 &a) { return a ? __builtin_ctzll(a) : 64; }
inline int msb(const u64 &a) { return a ? 63 - __builtin_clzll(a) : -1; }
template <typename T>
inline int gbit(const T &a, int i) {
  return (a >> i) & 1;
}
template <typename T>
inline void sbit(T &a, int i, bool b) {
  if (gbit(a, i) != b) a ^= T(1) << i;
}
constexpr long long PW(int n) { return 1LL << n; }
constexpr long long MSK(int n) { return (1LL << n) - 1; }
}  // namespace Nyaan

// inout
namespace Nyaan {

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

template <typename T>
ostream &operator<<(ostream &os, const vector<T> &v) {
  int s = (int)v.size();
  for (int i = 0; i < s; i++) os << (i ? " " : "") << v[i];
  return os;
}
template <typename T>
istream &operator>>(istream &is, vector<T> &v) {
  for (auto &x : v) is >> x;
  return is;
}

istream &operator>>(istream &is, __int128_t &x) {
  string S;
  is >> S;
  x = 0;
  int flag = 0;
  for (auto &c : S) {
    if (c == '-') {
      flag = true;
      continue;
    }
    x *= 10;
    x += c - '0';
  }
  if (flag) x = -x;
  return is;
}

istream &operator>>(istream &is, __uint128_t &x) {
  string S;
  is >> S;
  x = 0;
  for (auto &c : S) {
    x *= 10;
    x += c - '0';
  }
  return is;
}

ostream &operator<<(ostream &os, __int128_t x) {
  if (x == 0) return os << 0;
  if (x < 0) os << '-', x = -x;
  string S;
  while (x) S.push_back('0' + x % 10), x /= 10;
  reverse(begin(S), end(S));
  return os << S;
}
ostream &operator<<(ostream &os, __uint128_t x) {
  if (x == 0) return os << 0;
  string S;
  while (x) S.push_back('0' + x % 10), x /= 10;
  reverse(begin(S), end(S));
  return os << S;
}

void in() {}
template <typename T, class... U>
void in(T &t, U &...u) {
  cin >> t;
  in(u...);
}

void out() { cout << "\n"; }
template <typename T, class... U, char sep = ' '>
void out(const T &t, const U &...u) {
  cout << t;
  if (sizeof...(u)) cout << sep;
  out(u...);
}

void outr() {}
template <typename T, class... U, char sep = ' '>
void outr(const T &t, const U &...u) {
  cout << t;
  outr(u...);
}

struct IoSetupNya {
  IoSetupNya() {
    cin.tie(nullptr);
    ios::sync_with_stdio(false);
    cout << fixed << setprecision(15);
    cerr << fixed << setprecision(7);
  }
} iosetupnya;

}  // namespace Nyaan

// debug
namespace DebugImpl {

template <typename U, typename = void>
struct is_specialize : false_type {};
template <typename U>
struct is_specialize<
    U, typename conditional<false, typename U::iterator, void>::type>
    : true_type {};
template <typename U>
struct is_specialize<
    U, typename conditional<false, decltype(U::first), void>::type>
    : true_type {};
template <typename U>
struct is_specialize<U, enable_if_t<is_integral<U>::value, void>> : true_type {
};

void dump(const char& t) { cerr << t; }

void dump(const string& t) { cerr << t; }

void dump(const bool& t) { cerr << (t ? "true" : "false"); }

void dump(__int128_t t) {
  if (t == 0) cerr << 0;
  if (t < 0) cerr << '-', t = -t;
  string S;
  while (t) S.push_back('0' + t % 10), t /= 10;
  reverse(begin(S), end(S));
  cerr << S;
}

void dump(__uint128_t t) {
  if (t == 0) cerr << 0;
  string S;
  while (t) S.push_back('0' + t % 10), t /= 10;
  reverse(begin(S), end(S));
  cerr << S;
}

template <typename U,
          enable_if_t<!is_specialize<U>::value, nullptr_t> = nullptr>
void dump(const U& t) {
  cerr << t;
}

template <typename T>
void dump(const T& t, enable_if_t<is_integral<T>::value>* = nullptr) {
  string res;
  if (t == Nyaan::inf) res = "inf";
  if constexpr (is_signed<T>::value) {
    if (t == -Nyaan::inf) res = "-inf";
  }
  if constexpr (sizeof(T) == 8) {
    if (t == Nyaan::infLL) res = "inf";
    if constexpr (is_signed<T>::value) {
      if (t == -Nyaan::infLL) res = "-inf";
    }
  }
  if (res.empty()) res = to_string(t);
  cerr << res;
}

template <typename T, typename U>
void dump(const pair<T, U>&);
template <typename T>
void dump(const pair<T*, int>&);

template <typename T>
void dump(const T& t,
          enable_if_t<!is_void<typename T::iterator>::value>* = nullptr) {
  cerr << "[ ";
  for (auto it = t.begin(); it != t.end();) {
    dump(*it);
    cerr << (++it == t.end() ? "" : ", ");
  }
  cerr << " ]";
}

template <typename T, typename U>
void dump(const pair<T, U>& t) {
  cerr << "( ";
  dump(t.first);
  cerr << ", ";
  dump(t.second);
  cerr << " )";
}

template <typename T>
void dump(const pair<T*, int>& t) {
  cerr << "[ ";
  for (int i = 0; i < t.second; i++) {
    dump(t.first[i]);
    cerr << (i == t.second - 1 ? "" : ", ");
  }
  cerr << " ]";
}

void trace() { cerr << endl; }
template <typename Head, typename... Tail>
void trace(Head&& head, Tail&&... tail) {
  cerr << " ";
  dump(head);
  if (sizeof...(tail) != 0) cerr << ",";
  trace(forward<Tail>(tail)...);
}

}  // namespace DebugImpl

#ifdef NyaanDebug
#define trc(...)                            \
  do {                                      \
    cerr << "## " << #__VA_ARGS__ << " = "; \
    DebugImpl::trace(__VA_ARGS__);          \
  } while (0)
#else
#define trc(...) (void(0))
#endif

// macro
#define each(x, v) for (auto&& x : v)
#define each2(x, y, v) for (auto&& [x, y] : v)
#define all(v) (v).begin(), (v).end()
#define rep(i, N) for (long long i = 0; i < (long long)(N); i++)
#define repr(i, N) for (long long i = (long long)(N)-1; i >= 0; i--)
#define rep1(i, N) for (long long i = 1; i <= (long long)(N); i++)
#define repr1(i, N) for (long long i = (N); (long long)(i) > 0; i--)
#define reg(i, a, b) for (long long i = (a); i < (b); i++)
#define regr(i, a, b) for (long long i = (b)-1; i >= (a); i--)
#define fi first
#define se second
#define ini(...)   \
  int __VA_ARGS__; \
  in(__VA_ARGS__)
#define inl(...)         \
  long long __VA_ARGS__; \
  in(__VA_ARGS__)
#define ins(...)      \
  string __VA_ARGS__; \
  in(__VA_ARGS__)
#define in2(s, t)                           \
  for (int i = 0; i < (int)s.size(); i++) { \
    in(s[i], t[i]);                         \
  }
#define in3(s, t, u)                        \
  for (int i = 0; i < (int)s.size(); i++) { \
    in(s[i], t[i], u[i]);                   \
  }
#define in4(s, t, u, v)                     \
  for (int i = 0; i < (int)s.size(); i++) { \
    in(s[i], t[i], u[i], v[i]);             \
  }
#define die(...)             \
  do {                       \
    Nyaan::out(__VA_ARGS__); \
    return;                  \
  } while (0)

namespace Nyaan {
void solve();
}
int main() { Nyaan::solve(); }

//


template <typename mint>
std::pair<int, mint> GaussElimination(vector<vector<mint>> &a,
                                      int pivot_end = -1,
                                      bool diagonalize = false) {
  int H = a.size(), W = a[0].size();
  int rank = 0, je = pivot_end;
  if (je == -1) je = W;
  mint det = 1;
  for (int j = 0; j < je; j++) {
    int idx = -1;
    for (int i = rank; i < H; i++) {
      if (a[i][j] != mint(0)) {
        idx = i;
        break;
      }
    }
    if (idx == -1) {
      det = 0;
      continue;
    }
    if (rank != idx) {
      det = -det;
      swap(a[rank], a[idx]);
    }
    det *= a[rank][j];
    if (diagonalize && a[rank][j] != mint(1)) {
      mint coeff = a[rank][j].inverse();
      for (int k = j; k < W; k++) a[rank][k] *= coeff;
    }
    int is = diagonalize ? 0 : rank + 1;
    for (int i = is; i < H; i++) {
      if (i == rank) continue;
      if (a[i][j] != mint(0)) {
        mint coeff = a[i][j] / a[rank][j];
        for (int k = j; k < W; k++) a[i][k] -= a[rank][k] * coeff;
      }
    }
    rank++;
  }
  return make_pair(rank, det);
}


template <typename mint>
vector<vector<mint>> LinearEquation(vector<vector<mint>> a, vector<mint> b) {
  int H = a.size(), W = a[0].size();
  for (int i = 0; i < H; i++) a[i].push_back(b[i]);
  auto p = GaussElimination(a, W, true);
  int rank = p.first;

  for (int i = rank; i < H; ++i) {
    if (a[i][W] != 0) return vector<vector<mint>>{};
  }

  vector<vector<mint>> res(1, vector<mint>(W));
  vector<int> pivot(W, -1);
  for (int i = 0, j = 0; i < rank; ++i) {
    while (a[i][j] == 0) ++j;
    res[0][j] = a[i][W], pivot[j] = i;
  }
  for (int j = 0; j < W; ++j) {
    if (pivot[j] == -1) {
      vector<mint> x(W);
      x[j] = 1;
      for (int k = 0; k < j; ++k) {
        if (pivot[k] != -1) x[k] = -a[pivot[k]][j];
      }
      res.push_back(x);
    }
  }
  return res;
}



template <typename mint>
vector<vector<mint>> inverse_matrix(const vector<vector<mint>>& a) {
  int N = a.size();
  assert(N > 0);
  assert(N == (int)a[0].size());

  vector<vector<mint>> m(N, vector<mint>(2 * N));
  for (int i = 0; i < N; i++) {
    copy(begin(a[i]), end(a[i]), begin(m[i]));
    m[i][N + i] = 1;
  }

  auto [rank, det] = GaussElimination(m, N, true);
  if (rank != N) return {};

  vector<vector<mint>> b(N);
  for (int i = 0; i < N; i++) {
    copy(begin(m[i]) + N, end(m[i]), back_inserter(b[i]));
  }
  return b;
}




template <uint32_t mod>
struct LazyMontgomeryModInt {
  using mint = LazyMontgomeryModInt;
  using i32 = int32_t;
  using u32 = uint32_t;
  using u64 = uint64_t;

  static constexpr u32 get_r() {
    u32 ret = mod;
    for (i32 i = 0; i < 4; ++i) ret *= 2 - mod * ret;
    return ret;
  }

  static constexpr u32 r = get_r();
  static constexpr u32 n2 = -u64(mod) % mod;
  static_assert(r * mod == 1, "invalid, r * mod != 1");
  static_assert(mod < (1 << 30), "invalid, mod >= 2 ^ 30");
  static_assert((mod & 1) == 1, "invalid, mod % 2 == 0");

  u32 a;

  constexpr LazyMontgomeryModInt() : a(0) {}
  constexpr LazyMontgomeryModInt(const int64_t &b)
      : a(reduce(u64(b % mod + mod) * n2)){};

  static constexpr u32 reduce(const u64 &b) {
    return (b + u64(u32(b) * u32(-r)) * mod) >> 32;
  }

  constexpr mint &operator+=(const mint &b) {
    if (i32(a += b.a - 2 * mod) < 0) a += 2 * mod;
    return *this;
  }

  constexpr mint &operator-=(const mint &b) {
    if (i32(a -= b.a) < 0) a += 2 * mod;
    return *this;
  }

  constexpr mint &operator*=(const mint &b) {
    a = reduce(u64(a) * b.a);
    return *this;
  }

  constexpr mint &operator/=(const mint &b) {
    *this *= b.inverse();
    return *this;
  }

  constexpr mint operator+(const mint &b) const { return mint(*this) += b; }
  constexpr mint operator-(const mint &b) const { return mint(*this) -= b; }
  constexpr mint operator*(const mint &b) const { return mint(*this) *= b; }
  constexpr mint operator/(const mint &b) const { return mint(*this) /= b; }
  constexpr bool operator==(const mint &b) const {
    return (a >= mod ? a - mod : a) == (b.a >= mod ? b.a - mod : b.a);
  }
  constexpr bool operator!=(const mint &b) const {
    return (a >= mod ? a - mod : a) != (b.a >= mod ? b.a - mod : b.a);
  }
  constexpr mint operator-() const { return mint() - mint(*this); }

  constexpr mint pow(u64 n) const {
    mint ret(1), mul(*this);
    while (n > 0) {
      if (n & 1) ret *= mul;
      mul *= mul;
      n >>= 1;
    }
    return ret;
  }
  
  constexpr mint inverse() const { return pow(mod - 2); }

  friend ostream &operator<<(ostream &os, const mint &b) {
    return os << b.get();
  }

  friend istream &operator>>(istream &is, mint &b) {
    int64_t t;
    is >> t;
    b = LazyMontgomeryModInt<mod>(t);
    return (is);
  }
  
  constexpr u32 get() const {
    u32 ret = reduce(a);
    return ret >= mod ? ret - mod : ret;
  }

  static constexpr u32 get_mod() { return mod; }
};

template <typename T>
struct Binomial {
  vector<T> f, g, h;
  Binomial(int MAX = 0) : f(1, T(1)), g(1, T(1)), h(1, T(1)) {
    while (MAX >= (int)f.size()) extend();
  }

  void extend() {
    int n = f.size();
    int m = n * 2;
    f.resize(m);
    g.resize(m);
    h.resize(m);
    for (int i = n; i < m; i++) f[i] = f[i - 1] * T(i);
    g[m - 1] = f[m - 1].inverse();
    h[m - 1] = g[m - 1] * f[m - 2];
    for (int i = m - 2; i >= n; i--) {
      g[i] = g[i + 1] * T(i + 1);
      h[i] = g[i] * f[i - 1];
    }
  }

  T fac(int i) {
    if (i < 0) return T(0);
    while (i >= (int)f.size()) extend();
    return f[i];
  }

  T finv(int i) {
    if (i < 0) return T(0);
    while (i >= (int)g.size()) extend();
    return g[i];
  }

  T inv(int i) {
    if (i < 0) return -inv(-i);
    while (i >= (int)h.size()) extend();
    return h[i];
  }

  T C(int n, int r) {
    if (n < 0 || n < r || r < 0) return T(0);
    return fac(n) * finv(n - r) * finv(r);
  }

  inline T operator()(int n, int r) { return C(n, r); }

  template <typename I>
  T multinomial(const vector<I>& r) {
    static_assert(is_integral<I>::value == true);
    int n = 0;
    for (auto& x : r) {
      if(x < 0) return T(0);
      n += x;
    }
    T res = fac(n);
    for (auto& x : r) res *= finv(x);
    return res;
  }

  template <typename I>
  T operator()(const vector<I>& r) {
    return multinomial(r);
  }

  T C_naive(int n, int r) {
    if (n < 0 || n < r || r < 0) return T(0);
    T ret = T(1);
    r = min(r, n - r);
    for (int i = 1; i <= r; ++i) ret *= inv(i) * (n--);
    return ret;
  }

  T P(int n, int r) {
    if (n < 0 || n < r || r < 0) return T(0);
    return fac(n) * finv(n - r);
  }

  T H(int n, int r) {
    if (n < 0 || r < 0) return T(0);
    return r == 0 ? 1 : C(n + r - 1, r);
  }
};

//
using namespace Nyaan;
using mint = LazyMontgomeryModInt<998244353>;
// using mint = LazyMontgomeryModInt<1000000007>;
using vm = vector<mint>;
using vvm = vector<vm>;
Binomial<mint> C;

using namespace Nyaan;

void q() {
  inl(N);
  if(N==2)die(2);
  int M = (N + 1) * (N + 1);
  vvm a(M, vm(M));
  vm b(M, -1);

  mint inv = mint(N * (N - 1) / 2).inverse();
  auto idx = [&](int i, int j) { return i * (N + 1) + j; };

  rep(i, M) a[i][i] += -1;

  rep(i0, N + 1) rep(i1, N + 1) {
    int i2 = N - i0 - i1;
    if (i2 < 0) continue;

    if (i0 == 0 and i1 == 0) {
      a[0][0] = 1;
      b[0] = 0;
      continue;
    }

    vi v;
    rep(i, i0) v.push_back(0);
    rep(i, i1) v.push_back(1);
    rep(i, i2) v.push_back(2);
    trc(i0,i1,i2,v);

    rep(i, N) rep(j, i) {
      int j0 = i0, j1 = i1;
      vi vv{v[i], v[j]};
      each(k, vv) {
        if (k == 0) j0--, j1++;
        if (k == 1) j1--;
        if (k == 2) j0++;
      }
      a[idx(i0, i1)][idx(j0, j1)] += inv;
    }
  }
  trc(a);
  trc(b);
  trc(LinearEquation(a,b));
  trc(inverse_matrix(a));
  auto x = LinearEquation(a, b)[0];
  out(x[idx(N, 0)]);
}

void Nyaan::solve() {
  int T = 1;

  // in(T);

  while (T--) q();
}
accuracy: 1
time_ms: 10
memory_kb: 5880
final_rank: 7

code_snippet: #include<bits/stdc++.h>

using namespace std;
typedef unsigned long long int ull;
typedef long long int ll;
typedef pair<ll,ll> pll;
typedef long double D;
//typedef complex<D> P;
#define F first
#define S second
//const ll MOD=1000000007;
const ll MOD=998244353;

template<typename T,typename U>istream & operator >> (istream &i,pair<T,U> &A){i>>A.F>>A.S; return i;}
template<typename T>istream & operator >> (istream &i,vector<T> &A){for(auto &I:A){i>>I;} return i;}
template<typename T,typename U>ostream & operator << (ostream &o,const pair<T,U> &A){o<<A.F<<" "<<A.S; return o;}
template<typename T>ostream & operator << (ostream &o,const vector<T> &A){int i=A.size(); for(auto &I:A){o<<I<<(--i?" ":"");} return o;}
template<typename T,typename U>T & chmax(T &a,const U &b){if(a<b){a=b;} return a;}
template<typename T,typename U>T & chmin(T &a,const U &b){if(b<a){a=b;} return a;}

#ifndef Mod_Int_hpp
#define Mod_Int_hpp



template<long long int mod=1000000007>
struct Mod_Int{
    typedef long long int ll;
    typedef pair<ll,ll> pll;
    typedef Mod_Int<mod> M;
    ll a;
    
    ll mod_pow(ll a,ll x){
        a%=mod;
        ll ans=1;
        for(int i=0;i<63;i++){
            if(x>>i&1){ans*=a; ans%=mod;}
            a*=a;
            a%=mod;
        }
        return ans;
    }
    
    pll Ex_gcd(ll a,ll b){
        if(b==0){return {1,0};}
        pll ret=Ex_gcd(b,a%b);
        ret.F-=a/b*ret.S;
        return {ret.S,ret.F};
    }
    
    ll prime_R(ll a){
        return mod_pow(a,mod-2);
    }
    
    ll R(ll a){
        ll ret=Ex_gcd(a,mod).F;
        ret%=mod;
        if(ret<0){ret+=mod;}
        return ret;
    }
    
    Mod_Int(ll A=0):a(A){
        a%=mod;
        if(a<0){a+=mod;}
    }
    
    Mod_Int(const M &b):a(b.a){}
    
    M & operator += (const M &b){
        a+=b.a;
        if(a>=mod){a-=mod;}
        return *this;
    }
    
    M operator + (const M &b) const {
        M c=*this;
        return c+=b;
    }
    
    M & operator -= (const M &b){
        a-=b.a;
        if(a<0){a+=mod;}
        return *this;
    }

  M operator - () const {return Mod_Int(-a);}
    
    M operator - (const M &b) const {
        M c=*this;
        return c-=b;
    }
    
    M & operator *= (const M &b){
        (a*=b.a)%=mod;
        return *this;
    }
    
    M operator * (const M &b) const {
        M c=*this;
        return c*=b;
    }
    
    M & operator /= (const M &b){
        (a*=R(b.a))%=mod;
        return *this;
    }
    
    M operator / (const M &b) const {
        M c=*this;
        return c/=b;
    }
    
    M & mod_pow_equal(ll x){
        ll ans=1;
        while(x>0){
            if(x&1){ans*=a; ans%=mod;}
            a*=a;
            a%=mod;
            x>>=1;
        }
        a=ans;
        return *this;
    }
    
    M mod_pow(ll x){
        M c(a);
        return c.mod_pow_equal(x);
    }
    
    bool operator == (const M &b) const {return a==b.a;}
    
    bool operator != (const M &b) const {return a!=b.a;}
    
    bool operator <= (const M &b) const {return a<=b.a;}
    
    bool operator < (const M &b) const {return a<b.a;}
    
    bool operator > (const M &b) const {return a>b.a;}
    
    bool operator >= (const M &b) const {return a>=b.a;}
    
    M & operator = (const M &b){
        a=b.a;
        return *this;
    }
    
    M & operator = (const ll &b){
        (a=b)%=mod;
        if(a<0){a+=mod;}
        return *this;
    }
};

template<long long MOD>istream & operator >> (istream &i,Mod_Int<MOD> &A){ll a; cin>>a; A=Mod_Int<MOD>(a); return i;}
template<long long MOD>ostream & operator << (ostream &i,const Mod_Int<MOD> &A){i<<A.a; return i;}

using Int=Mod_Int<MOD>;

#endif /*Mod_Int_hpp*/

int sz=0;



//BEGIN CUT HERE
template<typename K>
struct Matrix{

  typedef vector<K> arr;
  typedef vector<arr> mat;
  
  mat dat;

  Matrix(size_t r,size_t c):dat(r,arr(c,K())){}
  Matrix(mat dat):dat(dat){}

  size_t size() const{return dat.size();}
  bool empty() const{return size()==0;}
  arr& operator[](size_t k){return dat[k];}
  const arr& operator[](size_t k) const {return dat[k];}

  static Matrix matmul(const Matrix &A,const Matrix &B){
    Matrix res(A.size(),B[0].size());
    for(int i=0;i<(int)A.size();i++)
      for(int j=0;j<(int)B[0].size();j++)
        for(int k=0;k<(int)B.size();k++)
          res[i][j]+=A[i][k]*B[k][j];
    return res;
  }

  static Matrix identity(size_t n){
    Matrix res(n,n);
    for(int i=0;i<(int)n;i++) res[i][i]=K(1);
    return res;
  }

  Matrix pow(long long n) const{
    assert(n>=0);
    assert(dat.size()==dat[0].size());
    Matrix a(dat),res=identity(size());
    while(n){
      if(n&1) res=matmul(res,a);
      a=matmul(a,a);
      n>>=1;
    }
    return res;
  }

  template<typename T>
  static bool is_zero(T x){return x==T(0);}

  template<typename T>
  static bool compare(T,T y){return !is_zero(y);}

  // assume regularity
  static Matrix gauss_jordan(const Matrix &A,const Matrix &B){
    int n=A.size(),l=B[0].size();
    Matrix C(n,n+l);
    for(int i=0;i<n;i++){
      for(int j=0;j<n;j++)
        C[i][j]=A[i][j];
      for(int j=0;j<l;j++)
        C[i][n+j]=B[i][j];
    }
    for(int i=0;i<n;i++){
      int p=i;
      for(int j=i;j<n;j++)
        if(compare(C[p][i],C[j][i])) p=j;
      swap(C[i],C[p]);
      if(is_zero(C[i][i])) return Matrix(0,0);
      for(int j=i+1;j<n+l;j++) C[i][j]/=C[i][i];
      for(int j=0;j<n;j++){
        if(i==j) continue;
        for(int k=i+1;k<n+l;k++)
          C[j][k]-=C[j][i]*C[i][k];
      }
    }
    Matrix res(n,l);
    for(int i=0;i<n;i++)
      for(int j=0;j<l;j++)
        res[i][j]=C[i][n+j];
    return res;
  }

  Matrix inv() const{
    Matrix B=identity(size());
    return gauss_jordan(*this,B);
  }

  static arr linear_equations(const Matrix &A,const arr &b){
    Matrix B(b.size(),1);
    for(int i=0;i<(int)b.size();i++) B[i][0]=b[i];
    Matrix tmp=gauss_jordan(A,B);
    arr res(tmp.size());
    for(int i=0;i<(int)tmp.size();i++) res[i]=tmp[i][0];
    return res;
  }

  K determinant() const{
    Matrix A(dat);
    K res(1);
    int n=size();
    for(int i=0;i<n;i++){
      int p=i;
      for(int j=i;j<n;j++)
        if(compare(A[p][i],A[j][i])) p=j;
      if(i!=p) swap(A[i],A[p]),res=-res;
      if(is_zero(A[i][i])) return K(0);
      res*=A[i][i];
      for(int j=i+1;j<n;j++) A[i][j]/=A[i][i];
      for(int j=i+1;j<n;j++)
        for(int k=i+1;k<n;k++)
          A[j][k]-=A[j][i]*A[i][k];
    }
    return res;
  }
};
//END CUT HERE

using Mat=Matrix<Int>;

Int binom(int a,int b){
  if(b==0){return 1;}
  Int ret=1;
  for(int i=0;i<b;i++){ret*=a-i; ret/=i+1;}
  return ret;
}

int main(){
  cin.tie(0);
  ios::sync_with_stdio(false);

  ll N;
  cin>>N;
  if(N==2){cout<<2<<endl; return 0;}
  vector<vector<int>> idx(N+1,vector<int>(N+1,-1));
  for(int i=0;i<=N;i++){
    for(int j=0;i+j<=N;j++){
      idx[i][j]=sz++;
    }
  }
  Mat A(sz,sz);
  int r=0;
  for(int i=0;i<=N;i++){
    for(int j=0;i+j<=N;j++){
      int k=N-i-j;
      if(i>=2){A[r][idx[i-2][j+2]]+=binom(i,2)/binom(N,2);}
      if(j>=2){A[r][idx[i][j-2]]+=binom(j,2)/binom(N,2);}
      if(k>=2){A[r][idx[i+2][j]]+=binom(k,2)/binom(N,2);}
      if(i>=1 && j>=1){A[r][idx[i-1][j]]+=Int(i)*j/binom(N,2);}
      if(j>=1 && k>=1){A[r][idx[i+1][j-1]]+=Int(j)*k/binom(N,2);}
      if(k>=1 && i>=1){A[r][idx[i][j+1]]+=Int(k)*i/binom(N,2);}
      r++;
    }
  }
  for(int i=0;i<sz;i++){A[0][i]=0;}
  for(int i=0;i<sz;i++){A[i][i]-=1;}
  for(auto &I:A.dat){for(auto &J:I){J*=-1;}}
  assert(A.determinant()!=0);
  Mat B=A.inv();
  Int ans=0;
  for(int i=1;i<sz;i++){ans+=B[idx[N][0]][i];}
  cout<<ans<<endl;

  return 0;
}
accuracy: 1
time_ms: 40
memory_kb: 5580
final_rank: 15