#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <list>
#include <map>
#include <set>
#include <unordered_set>
#include <unordered_map>
#include <algorithm>
#include <cmath>
#include <functional>
#include <utility>
#include <tuple>
#include <numeric>
#include <queue>
#include <cstring>
#include <sstream>
#include <iomanip>
#include <regex>
#include <stack>
#include <limits>
#include <climits>

using namespace std;

const int M = 998244353;

int dfs(int i, vector<int>& was, vector<vector<int>>& v) {
	was[i] = 1;
	int res = 1;
	for (int next : v[i]) {
		if (was[next] == 0) {
			res += dfs(next, was, v);
		}
	}
	return res;
}

vector<int> calcfact(int n) {
	vector<int> res(n+1);
	res[0] = 1;
	for (int i = 1; i <= n; ++i) {
		res[i] = (res[i - 1] * (long long)i) % M;
	}
	return res;
}

int main() {
	ios_base::sync_with_stdio(false);
	int n, k;
	cin >> n >> k;
	vector<vector<int>> a(n, vector<int>(n, 0));
	for (int i = 0; i < n; ++i)
		for (int j = 0; j < n; ++j)
			cin >> a[i][j];

	auto f = calcfact(n);
	vector<vector<int>> v(n);
	for (int i = 0; i < n; ++i) {
		for (int j = i + 1; j < n; ++j) {
			bool ok = true;
			for (int z = 0; z < n; ++z) {
				if (a[i][z] + a[j][z] > k) {
					ok = false;
					break;
				}
			}
			if (ok) {
				v[i].push_back(j);
				v[j].push_back(i);
			}
		}
	}

	int res = 1;

	vector<int> was(n, 0);
	for (int i = 0; i < n; ++i) {
		if (was[i] == 0) {
			int cnt = dfs(i, was, v);
			res = (res * (long long)f[cnt]) % M;
		}
	}

	v.assign(n, vector<int>(n, 0));
	for (int i = 0; i < n; ++i) {
		for (int j = i + 1; j < n; ++j) {
			bool ok = true;
			for (int z = 0; z < n; ++z) {
				if (a[z][i] + a[z][j] > k) {
					ok = false;
					break;
				}
			}
			if (ok) {
				v[i].push_back(j);
				v[j].push_back(i);
			}
		}
	}

	was.assign(n, 0);
	for (int i = 0; i < n; ++i) {
		if (was[i] == 0) {
			int cnt = dfs(i, was, v);
			res = (res * (long long)f[cnt]) % M;
		}
	}

	cout << res << endl;
	return 0;
}

#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#include <utility>
#include <set>
#include <map>
#include <cmath>
#include <queue>
#include <cstdio>
#include <limits>
#define rep(i,n) for(int i = 0; i < n; ++i)
#define rep1(i,n) for(int i = 1; i <= n; ++i)
using namespace std;
template<class T>bool chmax(T &a, const T &b) { if(a < b){ a = b; return 1; } return 0; }
template<class T>bool chmin(T &a, const T &b) { if(a > b){ a = b; return 1; } return 0; }
template<class T> inline int  sz(T &a) { return a.size(); }
using ll = long long; using ld = long double;
using pi = pair<int,int>; using pl = pair<ll,ll>;
using vi = vector<int>; using vvi = vector<vi>;
using vl = vector<ll>; using vvl = vector<vl>;
const int inf = numeric_limits<int>::max();
const ll infll = numeric_limits<ll>::max();
// Modint
// modint<MOD> で宣言
template<ll MOD>
struct modint{
  ll x;
  ll mod = MOD;
  modint(ll x=0):x(x%MOD){}
  modint& operator+=(const modint a){
    if((x+=a.x)>=MOD) x-=MOD;
    return *this;
  }
  modint& operator-=(const modint a){
    if((x += MOD-a.x)>=MOD) x-=MOD;
    return *this;
  }
  modint& operator*=(const modint a){
    (x*=a.x)%=MOD;
    return *this;
  }
  modint operator+(const modint a) const{
    modint res(*this);
    return res+=a;
  }
  modint operator-(const modint a) const{
    modint res(*this);
    return res-=a;
  }
  modint operator*(const modint a) const{
    modint res(*this);
    return res*=a;
  }
  modint pow(ll t) const{
    if(!t) return 1;
    modint a = pow(t>>1);
    a*=a;
    if(t&1) a*=*this;
    return a;
  }
  
  //for prime mod
  modint inv() const{
    return pow(MOD-2);
  }
  modint& operator/=(const modint a){
    return (*this) *= a.inv();
  }
  modint operator/(const modint a) const{
    modint res(*this);
    return res/=a;
  }
};
using mint = modint<998244353>;

const int NMAX=1000010; // we can calculate nCk until n is equal to NMAX
mint fact[NMAX],infac[NMAX];
void Make_Fact(){
  fact[0]=fact[1]=1;
  for(int i=2;i<=NMAX-1;++i){
    fact[i]=fact[i-1]*(mint)i;
  }
}
void Make_InvFact(){
  infac[0]=infac[1]=1;
  for(int i=2;i<=NMAX-1;++i){
    infac[i]=infac[i-1]/(mint)i;	
  }
}
mint Comb(int n,int k){
  if(n<0||k<0||n-k<0) return 0;
  return fact[n]*infac[k]*infac[n-k];
}




struct UFT{
  vector<int> par;//親
  vector<int> rank;//木の深さ
  vector<int> size;//木の大きさ
  int n;
  
  UFT(int _n)
  {
    n = _n;
    par.resize(n);
    rank.assign(n,0);
    size.assign(n,1);
    rep(i,n){
      par[i] = i;
    }
  }
  
  //xの根を返す
  int find(int x)
  {
    if(par[x] == x) return x;
    else return par[x] = find(par[x]);
  }

  //x,yを併合
  void unite(int x,int y)
  {
    x = find(x);
    y = find(y);
    if(x == y) return;

    if(rank[x] < rank[y]){
      par[x] = y;
      size[y] += size[x];
    }
    else{
      par[y] = x;
      size[x] += size[y];
      if(rank[x] == rank[y]) rank[x]++;
    }
  }

  //x,yが同じグループにいるかどうかを返す
  bool same(int x,int y)
  {
    return find(x) == find(y);
  }

  //xの属する木のサイズを探す
  int usize(int x)
  {
    return size[find(x)];
  }
};



int main()
{
  int n,k; cin >> n >> k;
  vvi a(n, vi(n));
  rep(i,n) {
    rep(j,n) {
      cin >> a[i][j];
    }
  }

  Make_Fact();
  UFT uf(2*n);
  rep(i,n) {
    rep(j,n) {
      if(i >= j) continue;
      bool f = true;
      rep(l,n) {
	if(a[i][l] + a[j][l] > k) f =false;
      }
      if(f) uf.unite(i, j);
    }
  }

  rep(i,n) {
    rep(j,n) {
      if(i >= j) continue;
      bool f = true;
      rep(l,n) {
	if(a[l][i] + a[l][j] > k) f =false;
      }
      if(f) uf.unite(i+n, j+n);
    }
  }  

  mint res(1);
  rep(i,2*n) {
    if(uf.find(i) == i) {
      res *= fact[uf.usize(i)];
    }
  }

  cout << res.x << "\n";
  
  return 0;
}
