#include <bits/stdc++.h>

#define rep(i,n) for(int i=0; i<(n); i++)
#define sz(x) int(x.size())

using namespace std;
typedef long long ll;
typedef pair<ll,int> P;

struct UnionFind{
	vector<int> par;

	UnionFind(int N){
		par.resize(N,-1);
	}

	int root(int x){
		if(par[x] < 0) return x;
		return par[x] = root(par[x]);
	}

	void unite(int x, int y){
		int rx = root(x);
		int ry = root(y);
		if(rx == ry) return;
		if(par[rx] < par[ry]){
			par[rx] += par[ry];
			par[ry] = rx;
		} else {
			par[ry] += par[rx];
			par[rx] = ry;
		}
		
	}

	bool same(int x, int y){
		return par[x] == par[y];
	}

    int size(int x){
        return (par[x] < 0) ? -par[x] : 0;
    }
};

int main(){ 
    const ll MOD = 998244353;
    int n,k; cin >> n >> k;
    vector<vector<int>> a(n,vector<int>(n));
    rep(i,n)rep(j,n) cin >> a[i][j];

    ll ans = 1;
    UnionFind uf1(n);
    for(int i=0; i<n; i++){
        for(int j=i+1; j<n; j++){
            bool f = true;
            for(int l = 0; l<n; l++){
                if(a[i][l]+a[j][l] > k) f = false;
            }
            if(f) uf1.unite(i,j); 
        }
    }
    for(int i=0; i<n; i++){
        ll t = uf1.size(i);
        while(t > 0){ ans = ans * t % MOD; t--; }
    }

    UnionFind uf2(n);
    for(int i=0; i<n; i++){
        for(int j=i+1; j<n; j++){
            bool f = true;
            for(int l = 0; l<n; l++){
                if(a[l][i]+a[l][j] > k) f = false;
            }
            if(f) uf2.unite(i,j); 
        }
    }

    for(int i=0; i<n; i++){
        ll t = uf2.size(i);
        while(t > 0){ ans = ans * t % MOD; t--; }
    }

    cout << ans << endl;



    return 0;
}

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

const long long MOD=998244353;
int N, K;

long long modmypower(long long a, long long b){
  if(b==1){
    return a;
  }else if(b%2==0){
    long long c=modmypower(a,b/2);
    return (c*c)%MOD;
  }else{
    long long c=modmypower(a,(b-1)/2);
    return (((c*c)%MOD)*a)%MOD;
  }
}   

long long modfactorial(long long x){
  if(x<=1) x=1;
  else for(int i=x-1; i>0; i--) x=((x*i)%MOD);
  return x;
}

long long modinv(long long x){
  return modmypower(x,MOD-2);
}

long long fn(vector<vector<int>>& mat){
  // 交換できる行のグラフをつくる
  vector<vector<int>> g(N, vector<int>());
  for(int i=0; i<N-1; i++){
    for(int j=i+1; j<N; j++){
      bool c=true;
      for(int k=0; k<N; k++) if(mat[i][k]+mat[j][k]>K) c=false;
      if(c){
        g[i].push_back(j);
        g[j].push_back(i);
      }
    }
  }
  
  long long result=1;
  
  //連結成分ごとに見る
  vector<int> visited(N, 0);
  for(int i=0; i<N; i++){
    //連結成分を抽出
    if(visited[i]==1) continue;
    visited[i]=1;
    
    queue<int> q;
    q.emplace(i);
    
    vector<int> group;
    group.push_back(i);
    
    while(!q.empty()){
      int a=q.front();
      q.pop();
      for(auto dest:g[a]){
        if(visited[dest]==1) continue;
        visited[dest]=1;
        q.emplace(dest);
        group.push_back(dest);
      }
    }
    
    //ベクトルが一致するかどうか
    int s=group.size();
    vector<int> d(s, 0);
    for(int j=0; j<s; j++){
      for(int k=0; k<s; k++){
        if(mat[j]==mat[k]) d[j]++;
      }
    }
    
    map<int, int> dlist;
    for(int j=0; j<s; j++) dlist[d[j]]++;
    
    long long tmp=modfactorial(s);
    for(auto kv: dlist){
      int k=kv.first;
      int v=kv.second/k;
      if(k==1) continue;
      tmp=(tmp*modinv(modfactorial(v)))%MOD;
    }
    
    result=(result*tmp)%MOD;
  }
  
  return result;
}

int main(){
  cin >> N >> K;
  vector<vector<int>> A(N, vector<int>(N));
  for(int i=0; i<N*N; i++) cin >> A[i/N][i%N];
  
  long long result=fn(A);
  
  vector<vector<int>> B(N, vector<int>(N));
  for(int i=0; i<N*N; i++) B[i/N][i%N]=A[i%N][i/N];
  result*=fn(B);
  
  cout << result%MOD << endl;
}