#include <bits/stdc++.h>   #pragma GCC optimize( O3 ,  unroll-all-loops )   #pragma GCC target( sse4.2 )   using namespace std;   ifstream in;   ofstream out;   const long long kk = 1000;   const long long ml = kk * kk;   const long long mod = 998244353;   const long long inf = ml * ml * ml + 7;   const int C = 5;   mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());   int n, nxt, root;   vector<int> p, comp, sz, edges;   set<pair<int, pair<int, int>>> e;   vector<vector<int>> kids;   bool viv = false;   bool big = false;   const long double PI = 3.1415926535897932384626433;   inline int rev(int val, int b) {     int res = 0;     for (int i = 0; i < b; i++)       if (val & (1 << i)) res |= (1 << (b - 1 - i));     return res;   }   vector<int> mul_stpd(const vector<int> &a, const vector<int> &b) {     vector<long long> res(a.size() + b.size() - 1);     for (int i = 0; i < b.size(); i++) {       for (int j = 0; j < a.size(); j++) {         res[i + j] += (long long)a[j] * b[i];         res[i + j] %= mod;       }     }     return vector<int>(res.begin(), res.end());   }   int pp(int v) {     if (p[v] == v) return v;     return p[v] = pp(p[v]);   }   bool full(int v) {     if (viv)       cout <<  at   << v <<   with sz =   << sz[v] <<   see   << edges[v] << endl;     return ((int)sz[v] - 1) * (sz[v]) == 2 * edges[v];   }   void unit(int v, int u) {     v = pp(v);     u = pp(u);     if (v == u) {       edges[v]++;       return;     }     if (full(v) && full(u)) {       if (viv) cout <<  Fully  << endl;       if (sz[v] < sz[u]) swap(v, u);       sz[v] += sz[u];       edges[v] += edges[u];       p[u] = v;       edges[v]++;       kids[nxt].push_back(comp[v]);       kids[nxt].push_back(comp[u]);       comp[v] = nxt++;     } else {       if (viv) cout <<  Not Fully  << endl;       if (sz[v] < sz[u]) swap(v, u);       if (full(v) && !full(u)) swap(v, u);       sz[v] += sz[u];       edges[v] += edges[u];       p[u] = v;       edges[v]++;       assert(comp[v] >= n);       if (full(u))         kids[comp[v]].push_back(comp[u]);       else {         for (auto s : kids[comp[u]]) kids[comp[v]].push_back(s);       }     }     root = comp[v];   }   void work(int v, int u) { unit(v, u); }   void show() {     for (int i = nxt - 1; i >= 0; i--) {       cout << i <<  :  ;       for (auto s : kids[i]) cout << s <<    ;       cout << endl;     }   }   vector<int> get(int v) {     if (viv) {       cout <<  get(  << v <<  )  << endl;     }     if (v < n) {       return vector<int>({0, 1});     }     vector<int> lres(1, 1);     for (auto s : kids[v]) {       lres = mul_stpd(lres, get(s));       if (viv) {         cout <<   tnow   << v <<  :  ;         for (auto i : lres) cout << i <<    ;         cout << endl;       }     }     while (lres.size() < 2) lres.push_back(0);     lres[1]++;     if (viv) {       cout << v <<  :  ;       for (auto i : lres) cout << i <<    ;       cout << endl;     }     return lres;   }   void solve() {     cin >> n;     nxt = n;     sz.resize(n, 1);     comp.resize(n);     p.resize(n);     edges.resize(n, 0);     for (int i = 0; i < n; i++) p[i] = i, comp[i] = i;     kids.resize(2 * n);     vector<pair<int, int>> use(n * (n - 1) / 2);     for (int i = 0; i < n; i++) {       for (int j = 0; j < n; j++) {         int w;         cin >> w;         if (i < j) {           use[w - 1] = {i, j};         }       }     }     for (auto p : use) work(p.first, p.second);     if (viv) show();     auto res = get(root);     while (res.size() < n + 1) res.push_back(0);     for (int i = 1; i <= n; i++) cout << res[i] <<    ;     cout << endl;   }   int main() {     ios_base::sync_with_stdio(0);     cin.tie(0);     cout.tie(0);     int t = 1;     while (t--) solve();     return 0;   }