#include <algorithm>
#include <iostream>
#include <tuple>
#include <vector>
using ll = int64_t;
class range {private: struct I{ll x;ll operator*(){return x;}bool operator!=(I& lhs){return x<lhs.x;}void operator++(){++x;}};I i,n;
public:range(ll n):i({0}),n({n}){}range(ll i,ll n):i({i}),n({n}){}I& begin(){return i;}I& end(){return n;}};
using namespace std;
constexpr ll mod = 998244353;

// Modulo calculus

// With Montgomery multiply
// Mod < 2^32, odd number
constexpr ll ceil_log2(ll x) {
  ll y = 0;
  while (x) {
    ++y;
    x /= 2;
  }
  return y;
}

constexpr ll calc_np(const ll Mod, const ll R) {
  ll res = 0;
  ll t = 0;
  ll r = R;
  ll i = 1;
  while (r > 1) {
    if ((t % 2) == 0) {
      t += Mod;
      res += i;
    }
    t /= 2;
    r /= 2;
    i *= 2;
  }
  return res;
}

template <ll Mod>
class ModIntMR {
 public:
  explicit constexpr ModIntMR(const ll x) : data(reduction(x * R2)) {}
  ModIntMR<Mod>& operator=(const ModIntMR<Mod>& that) {
    data = that.data;
    return *this;
  }
  explicit operator ll() const { return reduction(data); }
  ModIntMR<Mod>& operator+=(const ModIntMR<Mod>& that) {
    data += that.data;
    if (data >= Mod) data -= Mod;
    return *this;
  }
  ModIntMR<Mod>& operator-=(const ModIntMR<Mod>& that) {
    data -= that.data;
    if (data < 0) data += Mod;
    return *this;
  }
  ModIntMR<Mod>& operator*=(const ModIntMR<Mod>& that) {
    data = reduction(data * that.data);
    return *this;
  }
  friend ModIntMR<Mod> operator+(ModIntMR<Mod> lhs, const ModIntMR<Mod>& rhs) {
    lhs += rhs;
    return lhs;
  }
  friend ModIntMR<Mod> operator-(ModIntMR<Mod> lhs, const ModIntMR<Mod>& rhs) {
    lhs -= rhs;
    return lhs;
  }
  friend ModIntMR<Mod> operator*(ModIntMR<Mod> lhs, const ModIntMR<Mod>& rhs) {
    lhs *= rhs;
    return lhs;
  }
  friend ModIntMR<Mod> operator^(ModIntMR<Mod> lhs, const ll rhs) {
    if (rhs == 0) return ModIntMR<Mod>(1);
    else if ((rhs % 2) == 1) return (lhs ^ (rhs-1)) * lhs;
    else {
      const auto half = lhs ^ (rhs / 2);
      return half * half;
    }
  }
  ModIntMR<Mod>& operator^=(const ll that) {
    data = (*this ^ that).data;
    return *this;
  }
 private:
  ll data;
  static constexpr ll shift = ceil_log2(Mod);
  static constexpr ll R = 1ll << shift;
  static constexpr ll mask = R-1;
  static constexpr ll R2 = R*R%Mod;
  static constexpr ll Np = calc_np(Mod, R);
  static constexpr ll reduction(const ll T) {
    ll t = (T + ((T*Np)&mask)*Mod)>>shift;
    return t >= Mod ? t - Mod : t;
  }
};

using modint = ModIntMR<mod>;

ll inv(ll a,ll p){
  return ( a == 1 ? 1 : (1 - p*inv(p%a,a)) / a + p );
}

vector<modint> fact(2500001, modint(0));
vector<modint> factinv(2500001, modint(0));

modint comb(ll n, ll k) {
  const auto tmp = fact[n] * factinv[k];
  return tmp * factinv[n-k];
}
 
int main() {
  cin.tie(0);
  ios::sync_with_stdio(false);
  ll n,m;
  cin>>n>>m;
  ll mx = 3*m+n;
  fact[0] = modint(1);
  for (ll i : range(mx)) {
    fact[i+1] = fact[i] * modint(i+1);
  }
  factinv[mx] = modint(inv(static_cast<ll>(fact[mx]), mod));
  for (ll i = mx; i > 0; --i) {
    factinv[i-1] = factinv[i] * modint(i);
  }
  modint tmp = comb(3*m+n-1, n-1);
  modint sub(0);
  for (ll i = 2*m+1; i <= 3*m; ++i) {
    sub += comb(3*m-i+n-2, n-2);
  }
  sub *= modint(n);
  modint mno(0);
  for (ll i = m+1; i <= n; ++i) {
    ll rem = 3*m-i;
    if (rem < 0 || (rem%2) == 1) continue;
    mno += comb(rem/2+n-1, n-1) * comb(n, i);
  }
  modint res = tmp - sub - mno;
  cout << static_cast<ll>(res) << endl;
  return 0;
}



Measured memory_bytes: 39296