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


//#define int long long 


template <int mod = 1000000007>
struct modint{
    int x;
    modint(int y = 0) : x((y % mod + mod) % mod) { }
    friend modint operator^(modint a, long long b) {
        modint r = 1;
        for(; b; b >>= 1, a *= a) if(b & 1) r *= a;
        return r;
    }
    friend modint operator-(modint a) {return modint(0) - a;}
    friend modint operator!(modint a) {return a ^ (mod - 2);}
    modint& operator/=(modint const& b) {return *this *= !b;}
    friend bool operator==(modint a, modint b) {return a.x == b.x;}
    friend bool operator!=(modint a, modint b) {return a.x != b.x;}
    friend modint operator+(modint a, modint const& b) {return a += b;}
    friend modint operator-(modint a, modint const& b) {return a -= b;}
    friend modint operator*(modint a, modint const& b) {return a *= b;}
    friend modint operator/(modint a, modint const& b) {return a /= b;}
    modint& operator*=(modint const& b) {x = 1LL * x * b.x % mod; return *this;}
    friend ostream& operator<<(ostream& os, modint const& a) {return os << a.x;}
    modint& operator+=(modint const& b) {x += b.x; x = (x >= mod) ? x - mod : x;return *this;}
    modint& operator-=(modint const& b) {x = x >= b.x ? x - b.x : x - b.x + mod;return *this;}
};

using mint = modint <>;


mint nCr(int n, int m) {
    if(m > n) return mint(0);
    mint p = 1, q = 1;
    for(int i = n - m + 1; i <= n; i++) p *= i;
    for(int i = 1; i <= m; i++) q *= i;
    return p / q;
}

int32_t main() {
   
    ios_base :: sync_with_stdio(0); cin.tie(0); cout.tie(0);
    int n, m, sum = 0; cin >> n >> m;
    vector <int> v(n + 1);
    for(int i = 1; i <= n; i++) {
        cin >> v[i];
        sum += v[i];
    }

    if(sum > m) {
        cout << 0 << endl;
        return 0;
    }

    mint a = nCr(m + n, (sum + n));
    cout << a << endl;
}
#include <bits/stdc++.h>
#define rep(i,n) for (int i = 0; i < (n); ++i)
using namespace std;
typedef long long ll;
typedef pair<int, int> P;

const ll mod = 998244353;

vector<ll> fac(300001); //n!(mod mod)
vector<ll> ifac(300001); //k!^{M-2} (mod mod)
//a,bの範囲的にこれだけ配列を用意していけば十分

ll mpow(ll x, ll n){ //x^n(mod mod) ←普通にpow(x,n)では溢れてしまうため，随時mod計算
    ll ans = 1;
    while(n != 0){
        if(n&1) ans = ans*x % mod;
        x = x*x % mod;
        n = n >> 1;
    }
    return ans;
}
ll comb(ll a, ll b){ //aCbをmod計算
    if(a == 0 && b == 0)return 1;
    if(a < b || a < 0)return 0;
    ll tmp = ifac[a-b]* ifac[b] % mod;
    return tmp * fac[a] % mod;
}

// union by size + path having
class UnionFind {
public:
    vector <ll> par; // 各元の親を表す配列
    vector <ll> siz; // 素集合のサイズを表す配列(1 で初期化)

    // Constructor
    UnionFind(ll sz_): par(sz_), siz(sz_, 1LL) {
        for (ll i = 0; i < sz_; ++i) par[i] = i; // 初期では親は自分自身
    }
    void init(ll sz_) {
        par.resize(sz_);
        siz.assign(sz_, 1LL);  // resize だとなぜか初期化されなかった
        for (ll i = 0; i < sz_; ++i) par[i] = i; // 初期では親は自分自身
    }

    // Member Function
    // Find
    ll root(ll x) { // 根の検索
        while (par[x] != x) {
            x = par[x] = par[par[x]]; // x の親の親を x の親とする
        }
        return x;
    }

    // Union(Unite, Merge)
    bool merge(ll x, ll y) {
        x = root(x);
        y = root(y);
        if (x == y) return false;
        // merge technique（データ構造をマージするテク．小を大にくっつける）
        if (siz[x] < siz[y]) swap(x, y);
        siz[x] += siz[y];
        par[y] = x;
        return true;
    }

    bool issame(ll x, ll y) { // 連結判定
        return root(x) == root(y);
    }

    ll size(ll x) { // 素集合のサイズ
        return siz[root(x)];
    }
};

int main() {
    int N, K; cin >> N >> K;
    int a[N][N];
    rep(i, N){
        rep(j, N){
            cin >> a[i][j];
        }
    }

    fac[0] = 1;
    ifac[0] = 1;
    for(ll i = 0; i<300000; i++){
        fac[i+1] = fac[i]*(i+1) % mod; // n!(mod mod)
        ifac[i+1] = ifac[i]*mpow(i+1, mod-2) % mod; // k!^{mod-2} (mod mod) ←累乗にmpowを採用
    }

    ll ans = 1;

    UnionFind Ux(N);
    for (int i = 0; i < N-1; i++){
        for (int j = i+1; j < N; j++){
            bool check = true;
            for (int k = 0; k < N; k++){
                if(a[i][k] + a[j][k] > K){
                    check = false;
                    break;
                }
            }
            if(check){
                Ux.merge(i, j);
            }
        }
    }

    unordered_set<int> setx;
    for (int i = 0; i < N; i++){
        int x = Ux.root(i);
        if(!setx.count(x)){
            setx.insert(x);
            int y = Ux.size(x);
            ans *= fac[y];
            ans %= mod;
        }
    }

    UnionFind Uy(N);
    for (int i = 0; i < N-1; i++){
        for (int j = i+1; j < N; j++){
            bool check = true;
            for (int k = 0; k < N; k++){
                if(a[k][i] + a[k][j] > K){
                    check = false;
                    break;
                }
            }
            if(check){
                Uy.merge(i, j);
            }
        }
    }

    unordered_set<int> sety;
    for (int i = 0; i < N; i++){
        int x = Uy.root(i);
        if(!sety.count(x)){
            sety.insert(x);
            int y = Uy.size(x);
            ans *= fac[y];
            ans %= mod;
        }
    }

    cout << ans << endl;

    return 0;
}