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

// Modulo calculus

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

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

template <int Mod>
class ModIntMR {
 public:
  explicit constexpr ModIntMR(const int x) : data(reduction(static_cast<ll>(x) * R2)) {}
  ModIntMR<Mod>& operator=(const ModIntMR<Mod>& that) {
    data = that.data;
    return *this;
  }
  explicit operator int() 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(static_cast<ll>(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:
  int data;
  static constexpr int shift = ceil_log2(Mod);
  static constexpr int R = 1 << shift;
  static constexpr int mask = R-1;
  static constexpr int R2 = static_cast<ll>(R)*R%Mod;
  static constexpr int Np = calc_np(Mod, R);
  static constexpr int reduction(const ll T) {
    int 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(int n, int 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/2+n;
  fact[0] = modint(1);
  for (int i : range(mx)) {
    fact[i+1] = fact[i] * modint(i+1);
  }
  factinv[mx] = modint(inv(static_cast<int>(fact[mx]), mod));
  for (int i = mx; i > 0; --i) {
    factinv[i-1] = factinv[i] * modint(i);
  }
  modint sub(0);
  for (int i = 2*m+1; i <= 3*m; ++i) {
    sub += comb(3*m-i+n-2, n-2);
  }
  sub *= modint(n);
  modint mno(0);
  for (int i = m%2; i <= n && i <= m; i+=2) {
    ll rem = 3*m-i;
    if (rem < 0) break;
    mno += comb(rem/2+n-1, n-1) * comb(n, i);
  }
  modint res = mno - sub;
  cout << static_cast<int>(res) << endl;
  return 0;
}



Measured memory_bytes: 19840