#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
#include <cmath>

using namespace std;

#define mp make_pair
#define pb push_back
#define ll long long

//#define debug(x) ;
#define debug(x) cerr << #x << " = " << x << "\n";

ostream& operator<<(ostream& cerr, vector<ll> aux) {
  cerr << "[";
  for (auto e : aux) cerr << e << ' ';
  cerr << "]";
  return cerr;
}

const int maxN = 3011;
ll mod = 998244353;

int n, k;
ll dp[maxN][maxN]; // i frac, j value

void add(ll& a, ll b) {
  a += b;
  if (a >= mod) a -= mod;
}

int main()
{
  //freopen("test.in", "r", stdin);

  cin >> n >> k;

  dp[n][k] = 1;
  for (int i = n; i >= 1; i--) {
    for (int j = 1; j <= i; j++) {
      if (dp[i][j] == 0) continue;

      add(dp[i - 1][j - 1], dp[i][j]);
      if (2 * j <= i)
        add(dp[i][2 * j], dp[i][j]);

      //cerr << i << ' ' << j << ' ' << dp[i][j] << '\n';
    }
  }

  cout << dp[0][0];

  return 0;
}

#include <bits/stdc++.h>

using namespace std;

typedef long long i64;
typedef unsigned long long ui64;
typedef vector<i64> vi;
typedef vector<vi> vvi;
typedef pair<i64, i64> pi;

#define pb push_back
#define sz(a) i64((a).size())
#define all(c) (c).begin(), (c).end()
#define REP(s, e, i) for(i=(s); i < (e); ++i)
inline void RI(i64 &i) {scanf("%lld", &(i));}
inline void RVI(vi &v) { for(i64 i=0;i<sz(v);++i) { RI(v[i]); } }
inline void RVVI(vvi &vv) { for(i64 i=0;i<sz(vv);++i) { RVI(vv[i]); } }
inline void WI(const i64 &i) {printf("%lld\n", i);}
inline void WVI(const vi &v, char sep=' ') { for(i64 i=0;i<sz(v);++i) { if(i != 0){ printf("%c", sep); } printf("%lld", v[i]);} printf("\n"); }
inline void WS(const string &s) { printf("%s\n", s.c_str()); }
inline void WB(bool b, const string &yes, const string &no) { if(b){ WS(yes);} else { WS(no);} }
inline void YESNO(bool b) { WB(b, "YES", "NO"); }
inline void YesNo(bool b) { WB(b, "Yes", "No"); }
#define BUF_LENGTH 1000000
inline void RS(string &s) {static char buf[BUF_LENGTH]; scanf("%s", buf); s = buf;}
template<typename T> inline bool IN(T &S, const typename T::key_type &key) {
  return S.find(key) != S.end();
}
template<typename T> inline bool ON(const T &b, i64 idx) {
  return ((T(1) << idx) & b) != 0;
}

struct graph {
  using node_t = long long;
  using weight_t = long long;
  using edge_t = pair<node_t, weight_t>;
  
  graph(node_t n) {
    edges.resize(n);
  }
  virtual ~graph() {}
  void add_edge(node_t i, node_t j, weight_t w=1) {
    edges[i].push_back({j, w});
  }
  virtual vector<edge_t> get_edges(node_t n) const {
    assert(n < get_num_nodes());
    return edges[n];
  }
  int get_num_nodes() const {
    return int(edges.size());
  }
  vector<vector<edge_t>> edges;
};

// dijkstra
i64 dijkstra(const graph &g, graph::node_t start) {
  using node_t = graph::node_t;
  using distance_t = graph::weight_t;
  using flag_t = unsigned char;
  const distance_t unreachable = -1;
  using dn = pair<distance_t, node_t>;
  priority_queue<dn, vector<dn>, greater<dn>> pq;
  vector<distance_t> distances(g.get_num_nodes(), unreachable);
  for(auto &e: g.get_edges(start)) {
    node_t to = e.first;
    distance_t w = e.second;
    if(distances[to] == unreachable || w < distances[to]) {
      distances[to] = w;
    }
    pq.push({distances[to], to});
  }

  while(!pq.empty()) {    
    dn cur_p = pq.top(); pq.pop();
    int cur_d = cur_p.first, cur = cur_p.second;
    if(distances[cur] != cur_d) {
      continue;
    }
    for(auto &e: g.get_edges(cur)) {
      node_t to = e.first;
      distance_t w = e.second;
      //cerr << cur << " -> " << to << ": " << w << endl;
      if(distances[to] == unreachable || cur_d + w < distances[to]) {
        distances[to] = cur_d + w;
        pq.push({distances[to], to});
      }
    }
  }
  return distances[start];
}


int main(int argc, char *argv[]) {
  i64 i, j, k;  
  i64 N, M; cin >> N >> M;
  graph g(N);
  REP(0, M, i) {
    i64 a, b, c; RI(a); RI(b); RI(c); --a; --b;
    g.add_edge(a, b, c);
  }

  REP(0, N, i) {
    WI(dijkstra(g, i));
  }

  return 0;
}

