#include <bits/stdc++.h>   using namespace std;   const int64_t INF = 4e18, MOD = 998244353;   template <int64_t maxn>   struct NTT {     constexpr static int lg2(int n) { return 32 - __builtin_clz(n - 1); }     const static int MAXN = 1 << lg2(maxn), MOD = 998244353, root = 3;     int rev[MAXN], rt[MAXN];     int mul(int a, int b) { return (long long)a * b % MOD; }     int sub(int a, int b) { return b > a ? a - b + MOD : a - b; }     int add(int a, int b) { return a + b >= MOD ? a + b - MOD : a + b; }     int binExp(int base, long long exp) {       if (exp == 0) return 1;       return mul(binExp(mul(base, base), exp / 2), exp & 1 ? base : 1);     }     NTT() {       rt[1] = 1;       for (int k = 1; k < lg2(MAXN); k++) {         int z[] = {1, binExp(root, (MOD - 1) >> (k + 1))};         for (int i = (1 << k); i < 2 << k; i++) rt[i] = mul(rt[i / 2], z[i & 1]);       }     }     void ntt(int *a, int n) {       for (int i = 0; i < n; i++) rev[i] = (rev[i / 2] | (i & 1) << lg2(n)) / 2;       for (int i = 0; i < n; i++)         if (i < rev[i]) swap(a[i], a[rev[i]]);       for (int k = 1; k < n; k *= 2)         for (int i = 0; i < n; i += 2 * k)           for (int j = 0; j < k; j++) {             int z = mul(rt[j + k], a[i + j + k]);             a[i + j + k] = sub(a[i + j], z);             a[i + j] = add(a[i + j], z);           }     }     int in[2][MAXN];     vector<int> multiply(const vector<int> &a, const vector<int> &b) {       if (a.empty() || b.empty()) return {};       int sz = a.size() + b.size() - 1, n = 1 << lg2(sz);       fill(in[0], in[0] + n, 0), fill(in[1], in[1] + n, 0);       copy(a.begin(), a.end(), in[0]), copy(b.begin(), b.end(), in[1]);       ntt(in[0], n), ntt(in[1], n);       int invN = binExp(n, MOD - 2);       for (int i = 0; i < n; i++) in[0][i] = mul(mul(in[0][i], in[1][i]), invN);       reverse(in[0] + 1, in[0] + n);       ntt(in[0], n);       return vector<int>(in[0], in[0] + sz);     }   };   struct DSU {     int64_t n;     vector<int64_t> p, sz, cnt;     vector<bool> good;     vector<vector<int64_t>> g;     DSU(int64_t n) : n(n), p(n, -1), sz(n, 1), cnt(n, 0), good(n, 1), g(n) {}     int64_t find(int64_t c) {       if (p[c] == -1) return c;       return p[c] = find(p[c]);     }     void check(int64_t a) {       good.resize(p.size());       good[a] = (cnt[a] == (sz[a] * (sz[a] - 1)) / 2);     }     void uni(int64_t a, int64_t b) {       a = find(a), b = find(b);       if (a == b) {         cnt[a]++;         check(a);         return;       }       p[a] = p[b] = p.size();       p.push_back(-1);       sz.push_back(sz[a] + sz[b]);       cnt.push_back(cnt[a] + cnt[b] + 1);       g.resize(p.size());       g.back() = {a, b};       check(p.size() - 1);     }   };   void add(int32_t &a, int64_t b) { a = (a + b) % MOD; }   int64_t n, k;   vector<vector<int64_t>> g;   vector<vector<int32_t>> ans;   vector<int64_t> sz;   vector<bool> good;   NTT<2 * 1505> ntt;   void dfs(int64_t cur) {     for (auto nei : g[cur]) {       dfs(nei);     }     if (g[cur].size()) {       int64_t a = g[cur][0], b = g[cur][1];       ans[cur] = ntt.multiply(ans[a], ans[b]);     }     if (good[cur]) add(ans[cur][1], 1);     ans[cur].resize(sz[cur] + 1);   }   int32_t main() {     ios_base::sync_with_stdio(false);     cin >> n;     vector<vector<int64_t>> mat(n, vector<int64_t>(n));     vector<pair<int64_t, pair<int64_t, int64_t>>> edges;     for (int64_t i = 0; i < n; i++) {       for (int64_t j = 0; j < n; j++) {         cin >> mat[i][j];         if (j < i) edges.push_back({mat[i][j], {j, i}});       }     }     sort(edges.begin(), edges.end());     DSU dsu(n);     for (auto e : edges) {       dsu.uni(e.second.first, e.second.second);     }     g = dsu.g, good = dsu.good, sz = dsu.sz;     good.resize(g.size());     int64_t root = dsu.find(0);     k = n, n = g.size();     ans.resize(n, vector<int32_t>(k + 1));     dfs(root);     for (int64_t i = 1; i < k + 1; i++) cout << ans[root][i] <<    ;     return 0;   }   