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

typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;

template<int m>
struct modint {

	unsigned x;

	modint() : x(0) {}

	modint(long long arg) {
		arg %= m;
		if (arg < 0) {
			x = arg + m;
		} else {
			x = arg;
		}
	}	

	modint& operator+= (const modint& other) {
		x += other.x;
		if (x >= m) {
			x -= m;
		}
		return *this;
	}

	modint& operator*= (const modint& other) {
		x = (x * 1ull * other.x) % m;
		return *this;
	}

	modint& operator-= (const modint& other) {
		x += m - other.x;
		if (x >= m) {
			x -= m;
		}
		return *this;
	}

	modint operator+ (const modint& other) const {
		modint tmp = *this;
		tmp += other;
		return tmp;
	}

	modint operator- (const modint& other) const {
		modint tmp = *this;
		tmp -= other;
		return tmp;
	}

	modint operator* (const modint& other) const {
		modint tmp = *this;
		tmp *= other;
		return tmp;
	}

	explicit operator int () const {
		return x;
	}

	modint& operator++ () {
		++x;
		if (x == m) {
			x = 0;
		}
		return *this;
	}

	modint& operator-- () {
		if (x == 0) {
			x = m-1;
		} else {
			--x;
		}
		return *this;
	}

	modint operator++ (int) {
		modint tmp = *this;
		++*this;
		return tmp;
	}

	modint operator-- (int) {
		modint tmp = *this;
		--*this;
		return tmp;
	}

	bool operator== (const modint& other) const {
		return x == other.x;
	}

	bool operator!= (const modint& other) const {
		return x != other.x;
	}

	template<class T>
	modint operator^ (T arg) const {
		if (arg == 0) {
			return 1;
		}
		if (arg == 1) {
			return x;
		}
		auto t = *this ^ (arg >> 1);
		t *= t;
		if (arg & 1) {
			t *= *this;
		}
		return t;
	}

	template<class T>
	modint operator^= (T arg) {
		return *this = *this ^ arg;
	}

	modint inv() const {
		return *this ^ (m-2);
	}
};

const int MOD = 998244353;
typedef modint<MOD> mint;

mint f[4000005], finv[4000005];
int n, m;

mint bk(int n, int k) {
	if (n < 0 || k < 0 || n-k < 0)
		return 0;
	return f[n] * finv[k] * finv[n-k];
}

bool proveri(vector<int>& a) {
	int m = accumulate(a.begin(), a.end(), 0) / 3;
	if (*max_element(a.begin(), a.end()) > 2*m)
		return false;
	if ((int)(a.size() - count(a.begin(), a.end(), 0)) > 2*m)
		return false;
	if ((int)count(a.begin(), a.end(), 1) > m)
		return false;
	return true;
}

void rek1(vector<int>& a, set<vector<int>>& dobri, int dub) {
	if (dub == 0) {
		dobri.insert(a);
		return;
	}

	int n = a.size();
	for (int i=0; i<n; i++) {
		for (int j=0; j<n; j++) {
			if (i == j)
				continue;
			a[i] += 2;
			a[j] += 1;
			rek1(a, dobri, dub-1);
			a[i] -= 2;
			a[j] -= 1;
		}
	}
}

void rek2(vector<int>& a, set<vector<int>>& dobri, int dub) {
	if (dub == 0) {
		if (proveri(a))
			dobri.insert(a);
		return;
	}

	int n = a.size();
	for (int i=0; i<n; i++) {
		a[i] += 1;
		rek2(a, dobri, dub-1);
		a[i] -= 1;
	}
}


void proveri_hipotezu(int dub) {
	int n = 2*dub;
	vector<int> a(n, 0);
	set<vector<int>> s1, s2;
	rek1(a, s1, dub);
	rek2(a, s2, 3*dub);
	cerr << (s1 == s2) << '\n';
	cerr << s1.size() << '\n';
	cerr << s2.size() << '\n';

	for (auto v : s2) {
		if (s1.count(v) == 0) {
			for (int x : v)
				cerr << x << ' ';
			cerr << '\n';
		}
	}
}


/*
zbir = 3m-k
mesta = k

3m+k-1 k-1
*/

/*
zbir m-k
mesta = k
*/

/*
1 1 2 2
1 2 3
1 1 4
3 3
2 4
2 2 2
*/

int main() {
	ios_base::sync_with_stdio(false);
	cin.tie(nullptr);
	cout.tie(nullptr);
	cerr.tie(nullptr);

	f[0] = finv[0] = 1;
	for (int i=1; i<4000005; i++) {
		f[i] = f[i-1] * i;
		finv[i] = f[i].inv();
	}

	cin >> n >> m;
	mint sol;
	for (int k=2; k<=min(m, n); k++) {
		sol += (bk(3*m-1, k-1) - bk(m-1, k-1) * k) * bk(n, k);
	}
	for (int k=m+1; k<=n; k++) {
		sol += bk(3*m-1, k-1)* bk(n, k);
	}
	for (int u=m+2; u<=min(n, 3*m); u+=2) {
		mint q = bk(n, u) * bk((3*m-u)/2 + n-1, n-1);
		// cerr << u << ' ' << (int)q << ' ' << (3*m-u)/2 << ' ' << (int)bk(n, u) << '\n';
		sol -= q;
	}

	cout << (int)sol << '\n';
}

Measured memory_bytes: 31488