#include<bits/stdc++.h>
#define all(x) (x).begin(),(x).end()
#define ll long long
#define rep(i,n) for(int i = 0; i < int(n); i++)
#define vi vector<int>
using namespace std;
const int INF = 1001001001;
const int MOD = 998244353;
template<class T> inline bool chmax(T &a, const T &b){ if(a<b){ a=b; return 1; } return 0; }
template<class T> inline bool chmin(T &a, const T &b){ if(b<a){ a=b; return 1; } return 0; }

struct mint{ // auto MOD int
  ll x;
  mint(ll x=0) : x((x%MOD+MOD)%MOD){}  //コンストラクタ
  mint operator-() const { return mint(-x); } //符号変換
  mint operator+(const mint &a) const { return mint(*this) += a; } //算術演算子
  mint operator-(const mint &a) const { return mint(*this) -= a; }
  mint operator*(const mint &a) const { return mint(*this) *= a; }
  mint operator/(const mint &a) const { return mint(*this) /= a; }
  mint pow(ll n) const {  //繰り返し累乗
    if(!n) return 1;
    mint a = pow(n>>1);
    a *= a;
    if(n&1) a *= *this;
    return a;
  }
  mint inv() const { return pow(MOD-2); } // for prime mod
  mint& operator+=(const mint a){ if((x+=a.x) >= MOD) x-=MOD;  return *this; } //代入演算子
  mint& operator-=(const mint a){ if((x+=MOD-a.x) >= MOD) x-=MOD;  return *this; }
  mint& operator*=(const mint a){ (x *= a.x) %= MOD;  return *this; }
  mint& operator/=(const mint a){ return (*this) *= a.inv(); }
  friend ostream& operator<<(ostream &stout, const mint &tmp){ stout << tmp.x; return stout; }
};

class UnionFind{
public:
  vector<ll> parent;  //parent[i]はiの親
  vector<ll> siz;  //素集合のサイズを表す配列(1で初期化)
  UnionFind(ll n) : parent(n), siz(n,1){ rep(i,n) parent[i] = i; } //コンストラクタ  //全ての要素の根が自身であるとして初期化
  ll root(ll x){ //データxの属する木の根を取得(経路圧縮も行う)
    if(parent[x] == x) return x;
    return parent[x] = root(parent[x]);  //代入式の値は代入した変数の値なので、経路圧縮できる
  }
  void unite(ll x,ll y){ //xとyの木を併合
    ll rx = root(x);  //xの根
    ll ry = root(y);  //yの根
    if(rx == ry) return;  //同じ木にある時
    if(siz[rx] < siz[ry]) swap(rx,ry);
    siz[rx] += siz[ry];  //小さい集合を大きい集合へと併合(ry→rxへ併合)
    parent[ry] = rx;  //xとyが同じ木にない時はyの根ryをxの根rxにつける
  }
  bool same(ll x,ll y){ return root(x) == root(y); } //xとyが属する木が同じかを判定
  ll size(ll x){ return siz[root(x)]; } //xの素集合のサイズを取得
};

const int MAX = 1e6;
typedef vector<mint> vm;
struct comb {
  vm f, g;
  comb(){}
  comb(int mx):f(mx+1),g(mx+1) {
    f[0] = 1;
    for(int i = 1; i <= mx; ++i) f[i] = f[i-1]*i;
    g[mx] = f[mx].pow(MOD-2);
    for(int i=mx;i>0;i--) g[i-1] = g[i]*i;
  }
  mint operator()(int a, int b) {
    if (a < b) return 0;
    return f[a]*g[b]*g[a-b];
  }
} c(MAX);

int n,k;
vector<vi> a;
mint f(){
  UnionFind uf(n);
  rep(i,n) rep(j,i){
    bool ok = true;
    rep(K,n) if(a[i][K]+a[j][K] > k) ok = false;
    if(ok) uf.unite(i,j);
  }
  mint res = 1;
  rep(i,n) if(i == uf.root(i)) res *= c.f[uf.size(i)];
  return res;
}

int main(){
  cin.tie(0), ios::sync_with_stdio(false);
  cin >> n >> k;
  a = vector<vi>(n,vi(n)); rep(i,n) rep(j,n) cin >> a[i][j];
  mint ans = 1;
  rep(cnt,2){
    ans *= f();
    rep(i,n) rep(j,i) swap(a[i][j],a[j][i]);
  }
  cout << ans;
  cout << endl;
  return 0;
}

#include <iostream>
#include <algorithm>
#define MOD 998244353
int N, K;
int A[50][50];
int row_ok[50][50];
int col_ok[50][50];
long long num[51];
using namespace std;

long long calc(int line_ok[][50]) {
    long long ans = 1;
    int group[50];
    int groupN[50];
    for (int i=0; i<N; i++) group[i] = i;
    for (int i=0; i<N; i++) {
        groupN[i] = 0;
    }

    for (int _=0; _<=N; _++) {
        for (int i=0; i<N; i++) {
            for (int j=0; j<N; j++) {
                if (line_ok[i][j] == 1) {
                    int x = min(group[i], group[j]);
                    group[i] = group[j] = x;
                }
            }
        }
    }
    for (int i=0; i<N; i++) {
        groupN[group[i]]++;
    }
    for (int i=0; i<N; i++) {
        if (groupN[i] > 0) {
            ans *= num[groupN[i]];
            ans %= MOD;
        }
    }
    return ans;
}
int main() {
    {
        long long x = 1;
        for (int i=1; i<51; i++)
        {
            x *= i;
            x %= MOD;
            num[i] = x;
        }
    }

    cin >> N >> K;
    for (int i=0; i<N; i++) {
        for (int j=0; j<N; j++) {
            cin >> A[i][j];
        }
    }

    for (int i=0; i<N; i++) {
        for (int j=0; j<N; j++) {
            bool ok = true;
            for (int c=0; c<N; c++) {
                if (A[c][i] + A[c][j] > K) {
                    ok = false;
                    break;
                } 
            }
            if (ok) col_ok[i][j] = 1;
        }
    }
    
    for (int i=0; i<N; i++) {
        for (int j=0; j<N; j++) {
            bool ok = true;
            for (int c=0; c<N; c++) {
                if (A[i][c] + A[j][c] > K) {
                    ok = false;
                    break;
                } 
            }
            if (ok) row_ok[i][j] = 1;
        }
    }

    // row
    cout << (calc(row_ok) * calc(col_ok)) % MOD << endl;

    return 0;
}