#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef long double ld;
const ll M=998244353;
const ll MAXN=1e6+7;
const ld pi=acos(-1);

#define gi(n) ll n=read()
#define endl '\n'
#define rep(i,a,b) for(ll i=a; i<b; ++i)
#define per(i,a,b) for(ll i=b-1; i>=a; --i)

typedef vector<ll> V;
typedef vector<V> VV;
typedef set<ll> S;
typedef pair<ll,ll> pl;
typedef vector<pl> Vp;
typedef map<ll,ll> ml;

#define all(v) v.begin(),v.end()
#define rall(v) v.rbegin(),v.rend()
#define pb push_back
#define fs first
#define sc second

inline ll read(){
	ll x=0,ff=1;
	char ch=getchar();
	while(!isdigit(ch)) {if(ch=='-') ff=-1;ch=getchar();}
	while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*ff;
}

ll gcd(ll a, ll b){return b?gcd(b,a%b):a;}
ll pow1(ll p,ll d, ll P){return d?(pow1(p*p%P,d>>1,P)*(d&1?p%P:1))%P:1;}
//ll mul(ll a,ll b,ll P){return b?(mul(a*2%P,b>>1,P)+(b&1?a:0))%P:0;}
//ll pow1(ll p,ll d, ll P){return d?mul(pow1(mul(p,p,P),d>>1,P),(d&1?p%P:1),P):1;}

V prime;
bool is_composite[MAXN];
void sieve (ll n){
	std::fill (is_composite, is_composite + n, false);
	rep(i,2,n){
		if(!is_composite[i]) prime.pb(i);
		for(ll j = 0; j < (ll)prime.size() && i*prime[j] < n; ++j){
			is_composite[i*prime[j]] = true;
			if(i % prime[j] == 0) break;
		}
	}
}
ll modInverse(ll a, ll m){
	ll m0=m,y=0,x=1;
	if(m==1) return 0;
	while(a>1){
		ll q=a/m,t=m;
		m=a%m;
		a=t; 
		t=y;

		y = x-q*y; 
		x = t;
	}
	if(x<0) x += m0;
	return x; 
}

V fact(100007);

void dfs(ll v,V &visited, V &v1,VV w){ 
	visited[v] = true; 
	v1.pb(v);

	V::iterator i; 
	for(i = w[v].begin(); i != w[v].end(); ++i)
		if(!visited[*i]) 
			dfs(*i, visited,v1,w); 
} 

void doit(){
	gi(n);
	gi(l);
	VV a(n, V (n));
	rep (i,0,n)
		rep(j,0,n) a[i][j] = read();
	ll cr = 0;
	VV row(n),col(n);
	rep(i,0,n) {
		row[i].pb(i);
		rep(j,i+1,n){
			ll ok = 1;
			rep(k,0,n) if(a[i][k] + a[j][k] > l) ok = 0;
			if(ok) {
				row[i].pb(j);
				row[j].pb(i);
			}
		}
	}
	ll cc = 0;
	rep(i,0,n) {
		col[i].pb(i);
		rep(j,i+1,n){
			ll ok = 1;
			rep(k,0,n) if(a[k][i] + a[k][j] > l) ok = 0;
			if(ok) {
				col[i].pb(j);
				col[j].pb(i);
			}
		}
	}
	ll ans = 1;
//	cout << cr << " " << cc << "\n";
	V vis(n,0);
	V x[2*n];
	ll k = 0;
	rep(i,0,n)
		if(row[i].size())
			if(!vis[i])
				dfs(i,vis,x[k],row), k++;
	
	rep(i,0,n) vis[i] = 0;
	
	rep(i,0,n)
		if(col[i].size())
			if(!vis[i])
				dfs(i,vis,x[k],col), k++;
	
	
/*	rep(i,0,n) {
		if(row[i].size()) {
			cout << i << "\t";
			rep(j,0,row[i].size()) cout << row[i][j] << " ";
			cout << "\n";
		}
	}
	rep(i,0,n) {
		if(col[i].size()) {
			cout << i << "\t";
			rep(j,0,col[i].size()) cout << col[i][j] << " ";
			cout << "\n";
		}
	}
	
	rep (i,0,2*n) {
		for (auto j:x[i]) cout << j << " ";
		if(x[i].size())cout << "\n";
	}
*/	
	rep (i,0,2*n) {
		ans *= fact[x[i].size()];
		ans %= M;
	}
	
//	cr = (sqrt(8*cr)-1)/2;
//	cc = (sqrt(8*cc)-1)/2;
	cout << ans;
}

int main(){
	fact[0] = 1;
	rep (i,1,fact.size()) fact[i] = (fact[i-1] * i) % M;
//	for(gi(t);t--;cout<<endl)
		doit();
	return 0;
}
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MOD = 998244353;

class UnionFind {
public:
  vector<ll> par, siz;
  UnionFind(ll sz_) : par(sz_), siz(sz_, 1) {
    for(int i=0; i<sz_; ++i) par[i]=i;
  }
  void init(ll sz_) {
    par.resize(sz_);
    siz.assign(sz_,1);
    for(int i=0; i<sz_; ++i) par[i]=i;
  }

  ll root(ll x) {
    while(x!=par[x]) {
      x = par[x] = par[par[x]];
    }
    return x;
  }

  void merge(ll x, ll y) {
    x = root(x);
    y = root(y);
    if(x!=y) {
      if(siz[x]<siz[y]) swap(x,y);
      siz[x]+=siz[y];
      par[y]=x;
    }
  }

  bool issame(ll x, ll y) {
    return root(x) == root(y);
  }

  ll size(ll x) {
    return siz[root(x)];
  }
};

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

  //vector<bool> checkRow(N,false);
  //vector<bool> checkColumn(N,false);
  UnionFind ufRow(N), ufColumn(N);
  //ll rows, columns;
  for(int i=0; i<N-1; ++i) {
    for(int j=i+1; j<N; ++j) {
      bool canSwapR = true, canSwapC = true;
      for(int k=0; k<N; ++k) {
        if(A[i][k]+A[j][k] > K) canSwapC = false;
        if(A[k][i]+A[k][j] > K) canSwapR = false;
      }
      if(canSwapR) {
        //checkRow[i]=true; checkRow[j]=true;
        ufRow.merge(i,j);
      }
      if(canSwapC) {
        //checkColumn[i]=true; checkColumn[j]=true;
        ufColumn.merge(i,j);
      }
    }
  }
  //rows = count(checkRow.begin(),checkRow.end(),true);
  //columns = count(checkColumn.begin(),checkColumn.end(),true);
  // 各々の階乗をかければいいはず
  ll ans = 1;
  vector<bool> seen(N,false);
  for(int i=0; i<N; ++i) {
    if(!seen[ufRow.root(i)]) {
      for(int j=1; j<=ufRow.size(i); ++j) ans=ans*j%MOD;
      seen[ufRow.root(i)] = true;
    }
  }
  seen.assign(N,false);
  for(int i=0; i<N; ++i) {
    if(!seen[ufColumn.root(i)]) {
      for(int j=1; j<=ufColumn.size(i); ++j) ans=ans*j%MOD;
      seen[ufColumn.root(i)] = true;
    }
  }
  /*
  for(int i=1; i<=rows; ++i) ans=ans*i%MOD;
  for(int i=1; i<=columns; ++i) ans=ans*i%MOD;
  */
  cout << ans << endl;
}
