#include <bits/stdc++.h>
using namespace std;
const int Mod = 998244353, Primitive = 3;
struct FenwickTree {
  static const int Size = 2005;
  int a[Size];
  void update(int x, int v) {
    x++;
    while (x <= Size) a[x - 1] += v, x += x & (-x);
  }
  int query(int x) {
    int res = 0;
    x++;
    while (x >= 1) res += a[x - 1], x -= x & (-x);
    return res;
  }
  void init() { memset(a, 0, sizeof a); }
  int get(int x) { return query(x) - query(x - 1); }
};
inline int binomial(int a, int b) {
  if (a == b || b == 0) return 1;
  if (a < b) return 0;
  static int res[2005][2005];
  if (res[a][b] == 0)
    res[a][b] = (binomial(a - 1, b - 1) + binomial(a - 1, b)) % Mod;
  return res[a][b];
}
inline int fpow(int a, int b) {
  if (b == 0) return 1;
  long long res = fpow(a, b / 2);
  (res *= res) %= Mod;
  return b % 2 == 0 ? res : res * a % Mod;
}
inline int log2Ceil(int x) {
  int res = 0;
  x--;
  while (x) x /= 2, res++;
  return res;
}
void ntt(int n, int* a, int f) {
  static bool init = false;
  static int brev[4105];
  int bcnt = log2Ceil(n);
  assert(n == (1 << bcnt));
  for (int i = 1; i < n; i++) {
    brev[i] = (brev[i >> 1] >> 1) + ((i & 1) << (bcnt - 1));
    if (i < brev[i]) swap(a[i], a[brev[i]]);
  }
  for (int i = 2; i <= n; i *= 2) {
    int wn = fpow(Primitive, (Mod - 1) / i * (f == -1 ? i - 1 : 1)), w = 1;
    for (int k = 0; k < i / 2; k++) {
      for (int j = k; j < n; j += i) {
        int t = (long long)a[j + i / 2] * w % Mod;
        a[j + i / 2] = (a[j] + Mod - t) % Mod;
        a[j] = (a[j] + t) % Mod;
      }
      w = (long long)w * wn % Mod;
    }
  }
}
int n, a[2055][2055], fact[2055], derange;
int g[2055][2055];
void preprocess() {
  fact[0] = 1;
  for (int i = 1; i <= 2051; i++) fact[i] = (long long)fact[i - 1] * i % Mod;
  int len = 1 << (log2Ceil(n + 1) + 1), inv = fpow(len, Mod - 2);
  for (int i = 0; i <= n; i++) {
    static int p1[4105], p2[4105];
    memset(p1, 0, sizeof p1);
    memset(p2, 0, sizeof p2);
    for (int j = 0; j <= n; j++) {
      p1[j] = (j % 2 == 0) ? binomial(i, j) : (Mod - binomial(i, j)) % Mod;
      p2[j] = fact[j];
    }
    ntt(len, p1, 1);
    ntt(len, p2, 1);
    for (int j = 0; j < len; j++) g[i][j] = (long long)p1[j] * p2[j] % Mod;
    ntt(len, g[i], -1);
    for (int j = 0; j < len; j++) g[i][j] = (long long)g[i][j] * inv % Mod;
  }
}
int getRank(int* v) {
  static FenwickTree used;
  used.init();
  int res = 0;
  for (int i = 0; i < n; i++) {
    int curr = v[i] - used.query(v[i] - 1);
    (res += (long long)curr * fact[n - 1 - i] % Mod) %= Mod;
    used.update(v[i], 1);
  }
  return res;
}
int getRank(int* v, int* l) {
  static FenwickTree used, used2;
  used.init();
  used2.init();
  static bool f[2005];
  memset(f, false, sizeof f);
  int res = 0, rem = n;
  for (int i = 0; i < n; i++) {
    if (!f[l[i]]) f[l[i]] = true, rem--, used2.update(l[i], 1);
    int curr = v[i] - used.query(v[i] - 1),
        curr2 = v[i] - used2.query(v[i] - 1);
    if (rem > 0) (res += (long long)curr2 * g[rem - 1][n - 1 - i] % Mod) %= Mod;
    (res += (long long)(curr + Mod - curr2) * g[rem][n - 1 - i] % Mod) %= Mod;
    if (v[i] > l[i] && used.get(l[i]) == 0)
      (res += Mod - g[rem][n - 1 - i]) %= Mod;
    used.update(v[i], 1);
    if (!f[v[i]]) f[v[i]] = true, rem--, used2.update(v[i], 1);
  }
  return res;
}
int main() {
  scanf("%d", &n);
  for (int i = 0; i < n; i++)
    for (int j = 0; j < n; j++) {
      scanf("%d", a[i] + j);
      a[i][j]--;
    }
  preprocess();
  int res = 0;
  for (int i = 0; i < n; i++) {
    int curr = (long long)(i == 0 ? getRank(a[i]) : getRank(a[i], a[i - 1])) *
               fpow(g[n][n], n - 1 - i) % Mod;
    (res += curr) %= Mod;
  }
  printf("%d\n", res);
  return 0;
}
