#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#pragma GCC optimize("unroll-loops,no-stack-protector")
#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,tune=native")

using namespace std;
using namespace __gnu_pbds;
 
#define fi first
#define se second
#define mp make_pair
#define pb push_back
 
typedef long long ll;
typedef pair<ll,ll> ii;
typedef vector<int> vi;
typedef unsigned long long ull;
typedef long double ld; 
typedef tree<ii, null_type, less<ii>, rb_tree_tag, tree_order_statistics_node_update> pbds;

vector<int> fact;
vector<int> ifact;
vector<int> inv;
vector<int> pow2;
const int MOD = 998244353;
int add(int a, int b)
{
	a+=b;
	while(a>=MOD) a-=MOD;
	return a;
}
int mult(int a, int b)
{
	return (a*1LL*b)%MOD;
}
int modpow(int a, int b)
{
	int r=1;
	while(b)
	{
		if(b&1) r=mult(r,a);
		a=mult(a,a);
		b>>=1;
	}
	return r;
}
int choose(int a, int b)
{
	if(a<0) return 0;
	if(a<b) return 0;
	if(b<0) return 0;
	if(b==0) return 1;
	if(a==b) return 1;
	return mult(fact[a],mult(ifact[b],ifact[a-b]));
}
int inverse(int a)
{
	return modpow(a,MOD-2);
}
void init(int _n)
{
	fact.clear(); ifact.clear(); inv.clear(); pow2.clear();
	fact.resize(_n+1);
	ifact.resize(_n+1);
	inv.resize(_n+1);
	pow2.resize(_n+1);
	pow2[0]=1;
	ifact[0]=1;
	fact[0]=1;
	for(int i=1;i<=_n;i++)
	{
		pow2[i]=add(pow2[i-1],pow2[i-1]);
		fact[i]=mult(fact[i-1],i);
		//ifact[i]=mult(ifact[i-1],inv[i]);
	}
	ifact[_n] = inverse(fact[_n]);
	for(int i=_n-1;i>=1;i--)
	{
		ifact[i] = mult(ifact[i + 1], i + 1);
	}
	for(int i=1;i<=_n;i++)
	{
		inv[i] = mult(fact[i-1],ifact[i]);
	}
}
	
int under(int m, int n, int sum) //# of sequences of length n s.t. each term <= m, total = sum (sum<=2*m)
{
	int ans = choose(sum+n-1,n-1);
	ans=add(ans,MOD-mult(n,choose(sum-(m+1)+n-1,n-1)));
	return ans;
}

int main()
{
	ios_base::sync_with_stdio(0); cin.tie(0);
	init(3111111);
	int n,m; cin>>n>>m;
	int ans=0;
	/*
	//all <= m
	{
		int res = choose(3*m+n-1,n-1);
		//subtract 1 >m
		for(int i=m+1;i<=3*m;i++)
		{
			res=add(res,MOD-mult(n,under(m,n-1,3*m-i)));
		}
		//subtract 2 >m
		for(int s=2*m+2;s<=3*m;s++)
		{
			int coeff=choose(3*m-s+n-3,n-3);
			int l=m+1;
			int r=s-(m+1);
			int as=(r-l+1);
			int C = mult(choose(n,2),as);
			res=add(res,MOD-mult(coeff,C));
		}
		ans=add(ans,res);
	}
	//one > m
	
	//two > m
	{
		for(int s=2*m+2;s<=3*m;s++)
		{
			int coeff=choose(3*m-s+n-3,n-3);
			int l=m+1;
			int r=s-(m+1);
			int as=(r-l+1);
			int C = mult(choose(n,2),as);
			ans=add(ans,mult(coeff,C));
		}
	}
	*/
	for(int odd=0;odd<=m;odd++)
	{
		int coeff = choose(n,odd);
		int sum = 3*m-odd;
		if(sum%2!=0) continue;
		sum/=2;
		ans=add(ans,mult(coeff,choose(sum+n-1,n-1)));
		{
			//even i
			ans=add(ans,MOD-mult(n,mult(choose(n-1,odd),choose((m-2-odd)/2+n-1,n-1))));
		}
		if(odd>0)
		{
			//odd i
			ans=add(ans,MOD-mult(n,mult(choose(n-1,odd-1),choose((m-odd)/2+n-1,n-1))));
		}
		/*
		for(int i=2*m+1;i<=3*m;i++)
		{
			int oddrem = odd-(i%2);
			if(oddrem<0) continue;
			int ss = (3*m-i-oddrem);
			if(ss%2!=0) continue;
			ss/=2;
			ans=add(ans,MOD-mult(n,mult(choose(n-1,oddrem),choose(ss+n-2,n-2))));
		}
		*/
	}
	cout<<ans<<'\n';
}


Measured memory_bytes: 48896