#include<bits/stdc++.h>

#define DONTSYNC ios_base::sync_with_stdio(false);   cin.tie(NULL); cout.tie(NULL) //dont use stdio with iostream functions //input and output are out of order now!
#define TEST unsigned long long T; cin>>T; while(T--)  //loop over each testcase
#define endl "\n"
#define fori(a,start,end) for(int a=start;a<end;a++)
#define forll(a,start,end) for(long long a=start;a<end;a++)
#define forull(a,start,end) for(unsigned long long a=start;a<end;a++)
#define MOD 998244353
typedef long long ll;
typedef unsigned long long ull;

using namespace std;

int N,K;
int parent(int a,vector<int> &par){
    if(par[a]==a){
        return a;
    }
    par[a]=parent(par[a],par);
    return par[a];
}
void group(int a,int b,vector<int> &par){
    int para=parent(a,par),parb=parent(b,par);
    if(para<parb){
        par[parb]=para;
    }
    else{
        par[para]=parb;
    }
}
bool check(int isrow,int a,int b,vector< vector<int> > &mat){
    if(isrow){
        fori(col,0,N){
            if(mat[a][col]+mat[b][col]>K) return false;
        }
    }
    else{
        fori(row,0,N){
            if(mat[row][a]+mat[row][b]>K) return false;
        }
    }
    return true;
}
void solve(){
    /* code */
     cin>>N>>K;
    long long res=1;
    vector< vector<int> > mat(N,vector<int>(N));
    fori(i,0,N) fori(j,0,N) cin>>mat[i][j];
    ////////
    vector<int> par(N); fori(i,0,N) par[i]=i;
    fori(a,0,N-1){
        fori(b,a+1,N){
            if (check(1,a,b,mat)) group(a,b,par);
        }
    }
    vector<int> count(N,1);
    fori(i,0,N) res=(res*count[parent(i,par)]++)%MOD;
    //////
    fori(i,0,N) par[i]=i;
    fori(a,0,N-1){
        fori(b,a+1,N){
            if (check(0,a,b,mat)) group(a,b,par);
        }
    }
    fill(count.begin(),count.end(),1);
    fori(i,0,N) res=(res*count[parent(i,par)]++)%MOD;

    cout<<res;
}

int main()
{
    DONTSYNC;
    // TEST{
        solve();
    // }
    return 0;
}

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

typedef long long ll;
typedef pair<int,int> P;

const int INF=1e9;
const ll MOD=998244353;

struct UnionFind{

    vector<int> par;
    vector<int> sizes;

    UnionFind(int n):par(n),sizes(n,1){
        for(int i=0;i<n;i++){
            par[i]=i;
        }
    }
    
    int find(int x) { //xの根を得る
        if (x == par[x]) return x;
        return par[x] = find(par[x]); //根を張り替えながら再帰的に根ノードを探す
    }

    //2つの要素が属する集合を併合
    void unite(int x, int y) { 
        x = find(x);
        y = find(y);
        if (x == y) return;
        if (sizes[x] < sizes[y]) swap(x, y);
        par[y] = x;
        sizes[x] += sizes[y];
    }

    //2つの要素が同じ集合に属するか判定する
    bool same(int x, int y) {
        return find(x) == find(y);
    }

    //集合のサイズを取得
    int size(int x) {
        return sizes[find(x)];
    }
    
};

int main(){
    int N,K; cin >> N >> K;
    ll ans=1;
    ll a[N][N];
    for(int i=0;i<N;i++){
        for(int j=0;j<N;j++){
            cin >> a[i][j];
        }
    }
    UnionFind ufr(N+10);
    UnionFind ufl(N+10);
    vector<int> r[N+1],l[N+1];
    for(int i=0;i<N;i++){
        for(int j=i+1;j<N;j++){
            bool flag=true;
            bool flag2=true;
            for(int k=0;k<N;k++){
                if(i==j) continue;
                if((a[k][i]+a[k][j])>K) flag=false;
                if((a[i][k]+a[j][k])>K) flag2=false;    
            }
            if(flag) ufr.unite(i,j);
            if(flag2) ufl.unite(i,j);
        }
    }
    bool usedr[N+10]={};
    bool usedl[N+10]={};
    for(int i=0;i<N;i++){
        if(usedr[ufr.find(i)]) continue;
        usedr[ufr.find(i)]=true;
        for(int j=1;j<=ufr.size(ufr.find(i));j++){
           ans*=j; ans%=MOD; 
        }
    }
    for(int i=0;i<N;i++){
        if(usedl[ufl.find(i)]) continue;
        usedl[ufl.find(i)]=true;
        for(int j=1;j<=ufl.size(ufl.find(i));j++){
           ans*=j; ans%=MOD; 
        }
    }
    cout << ans%MOD << endl;
}