#include <bits/stdc++.h>
template <typename T>
struct xgcd_s {
  T m, n, gcd;
};
template <typename T>
xgcd_s<T> xgcd(T a, T b) {
  T m[] = {1, 0}, n[] = {0, 1}, q[] = {0, 0};
  for (int i = 0;; i ^= 1) {
    m[i] -= m[i ^ 1] * q[i];
    n[i] -= n[i ^ 1] * q[i];
    if (!b) return xgcd_s<T>{m[i], n[i], a};
    q[i] = a / b;
    a %= b;
    std::swap(a, b);
  }
}
template <unsigned M()>
struct _intmod {
 private:
  struct _p {
    unsigned x;
  };
  _intmod(_p x) { n = x.x; }

 public:
  unsigned n;
  _intmod() { n = 0; }
  _intmod(int x) { n = x < 0 ? M() + x % (int)M() : x % M(); }
  _intmod(unsigned x) { n = x % M(); }
  _intmod(long x) { n = unsigned(x < 0 ? M() + x % (long)M() : x % M()); }
  _intmod(unsigned long x) { n = x % M(); }
  _intmod(long long x) {
    n = unsigned(x < 0 ? M() + x % (long long)M() : x % M());
  }
  _intmod(unsigned long long x) { n = x % M(); }
  _intmod operator+(_intmod b) const { return {n + b.n}; }
  _intmod operator*(_intmod b) const {
    return {_p{unsigned((unsigned long long)n * b.n % M())}};
  }
  _intmod operator-(_intmod b) const {
    return {_p{n >= b.n ? n - b.n : M() + n - b.n}};
  }
  _intmod operator-() const { return {_p{n ? M() - n : 0U}}; }
  _intmod& operator+=(_intmod b) { return (*this = *this + b); }
  _intmod& operator-=(_intmod b) { return (*this = *this - b); }
  _intmod& operator/=(_intmod b) { return (*this = *this / b); }
  _intmod& operator*=(_intmod b) { return (*this = *this * b); }
  static _intmod mulinv(_intmod n) {
    xgcd_s<signed> x = xgcd<signed>(n.n, M());
    assert(x.gcd == 1);
    return x.m;
  }
  _intmod operator/(_intmod b) const { return *this * mulinv(b); }
  bool operator==(_intmod b) const { return n == b.n; }
  bool operator!=(_intmod b) const { return n != b.n; }
  explicit operator int() const { return n; }
};
template <unsigned M()>
std::ostream& operator<<(std::ostream& o, _intmod<M> n) {
  return o << n.n;
}
template <unsigned M()>
std::istream& operator>>(std::istream& i, _intmod<M>& n) {
  long long x;
  i >> x;
  n = x;
  return i;
}
template <unsigned M>
unsigned intmod_unsignedconst() {
  static_assert(M > 0 && M < (1U << 31), "modulus bad");
  return M;
}
template <unsigned* M>
unsigned intmod_unsignedptr() {
  return *M;
}
template <unsigned M>
using intmod = _intmod<intmod_unsignedconst<M> >;
template <unsigned* M>
using intmodv = _intmod<intmod_unsignedptr<M> >;
static_assert(std::is_trivially_copyable<intmod<3> >::value, "");
using I = intmod<998244353>;
using std::vector;
I factorial(int n) {
  static vector<I> fac = {1};
  while ((int)fac.size() <= n) fac.push_back(fac.back() * fac.size());
  return fac[n];
}
vector<I> solve_single_row(int n, int choices) {
  vector<I> a(choices + 1);
  a[0] = 1;
  for (int i = 0; i < choices; i++) {
    for (int j = i + 1; j > 0; j--) {
      a[j] = a[j] * (n - i - 1) + a[j - 1];
    }
    a[0] *= n - i - 1;
  }
  return a;
}
vector<I> solve_by_parameters(int n, int topBlocked, int topUnblocked,
                              int bottom, int empty, int preexistingCycles) {
  vector<I> withExtraBlocked;
  int bottomBlank = topBlocked + topUnblocked + empty;
  int emptySpots = topBlocked + topUnblocked + bottom + 2 * empty;
  int appearingOnce = topUnblocked + (bottom - topBlocked);
  int nowhere = (emptySpots - appearingOnce) / 2;
  if (topBlocked == 0) {
    withExtraBlocked.push_back(1);
  } else {
    withExtraBlocked.resize(topUnblocked + 1);
    for (int i = 0; i <= topUnblocked; i++) {
      withExtraBlocked[i] =
          factorial(nowhere) / factorial(nowhere - topBlocked) *
          factorial(topUnblocked) / factorial(topUnblocked - i) *
          factorial(i + topBlocked - 1) /
          (factorial(i) * factorial(topBlocked - 1));
    }
  }
  for (I& x : withExtraBlocked) x *= factorial(empty);
  vector<I> out(n);
  for (int i = 0; i < (int)withExtraBlocked.size(); i++) {
    int bBlank = topUnblocked - i + empty;
    int tBlank = bottom + empty;
    auto sr0 = solve_single_row(bBlank, topUnblocked - i);
    auto sr1 = solve_single_row(tBlank, tBlank);
    for (int c0 = 0; c0 <= topUnblocked - i; c0++)
      for (int c1 = 0; c1 <= tBlank; c1++) {
        I added = sr0[c0] * sr1[c1] * withExtraBlocked[i];
        if (added != 0) out[n - (preexistingCycles + c0 + c1)] += added;
      }
  }
  return out;
}
vector<I> solve(int n, vector<int> top, vector<int> bottom,
                vector<std::pair<int, int> > both) {
  vector<int> next(n + 1);
  for (auto p : both) next[p.first] = p.second;
  int topBlocked = 0;
  for (int b : bottom) {
    while (next[b] != 0) b = next[b];
    if (std::find(top.begin(), top.end(), b) != top.end()) ++topBlocked;
  }
  int cycles = 0;
  for (auto p : both) {
    int v = p.first;
    do {
      int nx = next[v];
      next[v] = 0;
      v = nx;
    } while (v != 0 && v != p.first);
    if (v != 0) ++cycles;
  }
  return solve_by_parameters(
      n, topBlocked, top.size() - topBlocked, bottom.size(),
      n - top.size() - bottom.size() - both.size(), cycles);
}
int main() {
  int n;
  std::cin >> n;
  vector<int> firstRow(n);
  vector<int> top, bottom;
  vector<std::pair<int, int> > both;
  for (int& x : firstRow) std::cin >> x;
  for (int x : firstRow) {
    int y;
    std::cin >> y;
    if (x && y)
      both.emplace_back(x, y);
    else if (x)
      top.push_back(x);
    else if (y)
      bottom.push_back(y);
  }
  auto ans = solve(n, top, bottom, both);
  for (I x : ans) std::cout << x << ' ';
  return 0;
}
