#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];
    int64_t nn = ans[a].size(), mm = ans[b].size();
    for (int64_t i = 1; i < nn; i++) {
      for (int64_t j = 1; j < mm; j++) {
        if (i + j > k) break;
        add(ans[cur][i + j], int64_t(ans[a][i]) * ans[b][j] % MOD);
      }
    }
  }
  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;
}
