#include<bits/stdc++.h>
using namespace std;
using ll=long long;
#define fr(i,n) for(int i=0;i<(n);++i)
#define Fr(i,n) for(int i=1;i<=(n);++i)
#define ifr(i,n) for(int i=(n)-1;i>=0;--i)
#define iFr(i,n) for(int i=(n);i>0;--i)
using Graph = vector<vector<int>>;

int main(){
    cin.tie(nullptr);
    ios::sync_with_stdio(false);
    // 頂点数と辺数
    int N,k; cin >> N>>k;
    int arr[N][N];
    fr(i,N){
        fr(l,N){
            int a;
            cin>>a;
            arr[i][l]=a;
        }
    }
    // グラフ入力受取 (ここでは無向グラフを想定)
    ll conbu[50];
    conbu[0]=1;
    Fr(i,49){
        conbu[i]=((conbu[i-1]*(i+1))%998244353);
    }
    bool arrx[N][N]={};
    bool arry[N][N]={};
    fr(i,N){
        fr(l,N){
            for(int g=i+1;g<(N);++g){
               if(arr[i][l]+arr[g][l]>k) arrx[i][g]=1; 
            }
            for(int g=l+1;g<(N);++g){
               if(arr[i][l]+arr[i][g]>k) arry[l][g]=1; 
            }
        }
    }
    Graph Gx(N);
    Graph Gy(N);
    fr(i,N){
        for(int l=i+1;l<(N);++l){
            if(arrx[i][l]==0){
            Gx[l].push_back(i);
            Gx[i].push_back(l);
            }
            if(arry[i][l]==0){
            Gy[l].push_back(i);
            Gy[i].push_back(l);
            }
        }
    }
    vector<int> distx(N, -1);
    queue<int> que;
    ll ans=1;
    int count=1;
    for (int v = 0; v < N; ++v) {
        if (distx[v] != -1) continue; // v が探索済みならスルー
        distx[v] = 0, que.push(v);
        while (!que.empty()) {
            int v = que.front(); que.pop();
            for (auto nv : Gx[v]) {
                if (distx[nv] == -1) {
                    distx[nv] = distx[v] + 1;
                    que.push(nv);
                    ++count;
                }
            }
        }
        ans=(ans*conbu[count-1])%998244353;
        count=1;
    }
    vector<int> disty(N, -1);
    for (int v = 0; v < N; ++v) {
        if (disty[v] != -1) continue; // v が探索済みならスルー
        disty[v] = 0, que.push(v);
        while (!que.empty()) {
            int v = que.front(); que.pop();
            for (auto nv : Gy[v]) {
                if (disty[nv] == -1) {
                    disty[nv] = disty[v] + 1;
                    que.push(nv);
                    ++count;
                }
            }
        }
        ans=(ans*conbu[count-1])%998244353;
        count=1;
    }
    cout<<ans;
}
#include <bits/stdc++.h>
using namespace std;

struct UnionFind
{
  vector<int> par; //par[i]はiの親の番号
  
  UnionFind(int N) : par(N)
  {
    for(int i = 0; i < N; i++) par[i] = i; //最初は全て根として初期化
  }
  
  int root(int x) //データxが属する木の根を再帰で得る root(x)={xの木の根}
  {
    if(par[x] == x) return x;
    return par[x] = root(par[x]); //親を再帰的にたぐり根を探す
  }
  
  void unite(int x, int y) //xとyの木を併合
  {
    int rx = root(x); //xの根
    int ry = root(y); //yの根
    if(rx == ry) return; //xとyの根が同じ(同じ木にある)ならそのまま
    par[rx] = ry; //xとyの根が同じでない(同じ木にない)ならyの根にxの根を付ける
  }
  
  bool same(int x, int y)//2つのデータx,yが同じ木に属するならtrue
  {
    int rx = root(x);
    int ry = root(y);
    return rx == ry;
  }
};

int main()
{
  int N, K;
  cin >> N >> K;
  vector<vector<int>> A(N, vector<int>(N));
  
  int64_t mod = 998244353;
  int64_t total = 1;
  
  bool pos = true;
  
  for(int i = 0; i < N; i++) for(int j = 0; j < N; j++) cin >> A.at(i).at(j);
  
  vector<int64_t> fact(50);
  fact.at(0) = 1;
  for(int i = 1; i <= 49; i++)
  {
    fact.at(i) = fact.at(i - 1) * i;
    fact.at(i) %= mod;
  }
  
  UnionFind treeX(N);
  UnionFind treeY(N);
  
  //行
  for(int i = 0; i < N - 1; i++)
  {
    for(int j = i + 1; j < N; j++)
    {
      for(int k = 0; k < N; k++) 
      {
        if(A.at(i).at(k) + A.at(j).at(k) > K)
        {
          pos = false;
          break;
        }
      }
      if(pos) treeX.unite(i, j);
      pos = true;
    }
  }
  //列
  for(int i = 0; i < N - 1; i++)
  {
    for(int j = i + 1; j < N; j++)
    {
      for(int k = 0; k < N; k++) 
      {
        if(A.at(k).at(i) + A.at(k).at(j) > K)
        {
          pos = false;
          break;
        }
      }
      if(pos) treeY.unite(i, j);
      pos = true;
    }
  }
  
  vector<int64_t> X(N), Y(N);
  for(int i = 0; i < N; i++)
  {
    X.at(i)++;
    if(treeX.root(i) != i) X.at(treeX.root(i))++;
    Y.at(i)++;
    if(treeY.root(i) != i) Y.at(treeY.root(i))++;
  }
  
  for(int i = 0; i < N; i++)
  {
    total *= fact.at(X.at(i));
    total %= mod;
  }
  for(int i = 0; i < N; i++)
  {
    total *= fact.at(Y.at(i));
    total %= mod;
  }
  
  cout << total << endl;
}