#include <bits/stdc++.h>
using namespace std;
template <typename T>
struct outputer;
struct outputable {};
template <typename T>
inline auto sqr(T x) -> decltype(x * x) {
  return x * x;
}
template <typename T1, typename T2>
inline bool umx(T1 &a, T2 b) {
  if (a < b) {
    a = b;
    return 1;
  }
  return 0;
}
template <typename T1, typename T2>
inline bool umn(T1 &a, T2 b) {
  if (b < a) {
    a = b;
    return 1;
  }
  return 0;
}
const int N = 1500;
const int64_t mod = 998244353;
struct Input {
  int n;
  int a[N][N];
  bool read() {
    if (!(cin >> n)) {
      return 0;
    }
    for (int i = int(0); i < int(n); ++i) {
      for (int j = int(0); j < int(n); ++j) {
        scanf("%d", &a[i][j]);
      }
    }
    return 1;
  }
  void init(const Input &input) { *this = input; }
};
struct Data : Input {
  vector<int64_t> ans;
  void write() {
    for (int i = int(1); i < int(((int)(ans).size())); ++i) {
      if (i > 1) {
        printf(" ");
      }
      printf("%" PRId64, ans[i]);
    }
    puts("");
  }
};
namespace Main {
struct Solution : Data {
  uint64_t rnd[N];
  int ord[N][N];
  uint64_t hash[N][N];
  bool good[N][N];
  map<uint64_t, vector<uint64_t>> e;
  uint64_t rand_ull() {
    uint64_t res = 0;
    for (int i = int(0); i < int(10); ++i) {
      res *= 1000000009;
      res += uint64_t(rand());
    }
    return res;
  }
  vector<int64_t> mult(const vector<int64_t> &lhs, const vector<int64_t> &rhs) {
    vector<int64_t> res(((int)(lhs).size()) + ((int)(rhs).size()) - 1, 0);
    for (int i = int(0); i < int(((int)(lhs).size())); ++i) {
      for (int j = int(0); j < int(((int)(rhs).size())); ++j) {
        res[i + j] = (res[i + j] + lhs[i] * rhs[j]) % mod;
      }
    }
    return res;
  }
  vector<int64_t> calc(uint64_t v) {
    ;
    ;
    vector<int64_t> res = {1};
    sort((e[v]).begin(), (e[v]).end());
    e[v].erase(unique((e[v]).begin(), (e[v]).end()), e[v].end());
    ;
    if (((int)(e[v]).size()) == 0) {
      return {0, 1};
    }
    for (uint64_t to : e[v]) {
      res = mult(res, calc(to));
    }
    res[1] = 1;
    return res;
  }
  void solve() {
    for (int i = int(0); i < int(n); ++i) {
      rnd[i] = rand_ull();
    }
    for (int i = int(0); i < int(n); ++i) {
      for (int j = int(0); j < int(n); ++j) {
        ord[i][j] = j;
      }
      sort(ord[i], ord[i] + n, [&](int x, int y) { return a[i][x] < a[i][y]; });
      uint64_t sum = 0;
      for (int j = int(0); j < int(n); ++j) {
        sum += rnd[ord[i][j]];
        hash[i][j] = sum;
      }
    }
    for (int i = int(0); i < int(n); ++i) {
      ;
    }
    for (int j = int(0); j < int(n); ++j) {
      unordered_map<uint64_t, int> cnt;
      unordered_map<uint64_t, int> lmx;
      unordered_map<uint64_t, int> rmn;
      const int inf = N * N;
      for (int i = int(0); i < int(n); ++i) {
        cnt[hash[i][j]]++;
        umx(lmx[hash[i][j]], a[i][ord[i][j]]);
        if (j + 1 < n) {
          umn(rmn[hash[i][j]], a[i][ord[i][j + 1]] - inf);
        }
      }
      for (int i = int(0); i < int(n); ++i) {
        good[i][j] = (cnt[hash[i][j]] == j + 1 &&
                      lmx[hash[i][j]] < rmn[hash[i][j]] + inf);
      }
    }
    for (int i = int(0); i < int(n); ++i) {
      ;
    }
    uint64_t root = accumulate(rnd, rnd + n, 0ull);
    for (int i = int(0); i < int(n); ++i) {
      assert(good[i][0]);
      assert(good[i][n - 1]);
      assert(hash[i][n - 1] == root);
      uint64_t lst = 0;
      for (int j = int(0); j < int(n); ++j) {
        if (good[i][j]) {
          uint64_t cur = hash[i][j];
          if (j) {
            e[cur].emplace_back(lst);
          }
          lst = cur;
        }
      }
    };
    ;
    ans = calc(root);
  }
  void clear() { *this = Solution(); }
};
}  // namespace Main
Main::Solution sol;
int main() {
  cout.setf(ios::showpoint | ios::fixed);
  cout.precision(20);
  sol.read();
  sol.solve();
  sol.write();
  return 0;
}
