#![allow(dead_code, unused_imports, unused_macros, non_snake_case)] fn main() { input! { N: usize, M: usize, } if N % 3 != 0 { say(0); return; } let n = N / 3; // binom(3n, n)/(2n+1) let sieve = LinearSieve::new(3 * n + 1); let mut pd = vec![0; 3 * n + 1]; for k in 1 ..= 3 * n { for &(p, e) in &sieve.prime_division_pairs(k) { pd[p] += e; } } for k in 1 ..= n { for &(p, e) in &sieve.prime_division_pairs(k) { pd[p] -= e; } } for k in 1 ..= 2 * n { for &(p, e) in &sieve.prime_division_pairs(k) { pd[p] -= e; } } for &(p, e) in &sieve.prime_division_pairs(2 * n + 1) { pd[p] -= e; } let mut ans = 1; for p in 0 ..= 3 * n { for _ in 0 .. pd[p] { ans *= p; ans %= M; } } say(ans); } type Int = usize; const MOD: Int = 998244353; // const MOD: Int = 1_000_000_007; const INF: Int = 1_000_000_000_000_000_000; const YESNO: [&'static str; 2] = ["Yes", "No"]; use proconio::{input, input_interactive, marker::{Chars, Bytes, Usize1}}; use std::*; use std::ops::*; use collections::*; // (BTree|Hash)(Set|Map), BinaryHeap, VecDeque, LinkedList use cmp::{self, Reverse}; // cmp::{min, max} fn yes() { println!("{}", YESNO[0]); } fn no() { println!("{}", YESNO[1]); } fn yesno(c: bool) { println!("{}", if c { YESNO[0] } else { YESNO[1] }); } fn say(x: T) -> T { println!("{}", x); x } fn neighbor4(i: usize, j: usize, h: usize, w: usize, mut f: F) { if i > 0 { (f)(i - 1, j); } if i < h - 1 { (f)(i + 1, j); } if j > 0 { (f)(i, j - 1); } if j < w - 1 { (f)(i, j + 1); } } trait MyItertools : Iterator + Sized { fn to_vec(self) -> Vec { self.collect::>() } fn to_vec_rev(self) -> Vec { let mut v = self.collect::>(); v.reverse(); v } fn tally(self) -> HashMap where Self::Item: Copy + Eq + hash::Hash { let mut counts = HashMap::new(); self.for_each(|item| *counts.entry(item).or_default() += 1 ); counts } fn count_if bool>(self, predicate: P) -> usize { self.map(predicate).filter(|&x| x ).count() } fn implode(self, sep: &str) -> String where Self::Item: std::string::ToString { self.map(|x| x.to_string()).to_vec().join(sep) } fn mex(self, gen: impl IntoIterator) -> Self::Item where Self::Item: Ord { let mut v = self.collect::>(); v.sort(); v.dedup(); let mut it = v.into_iter(); gen.into_iter().find(|a| if let Some(x) = it.next() { a != &x } else { true }).unwrap() } } impl MyItertools for T where T: Iterator + Sized {} trait MyOrd : PartialOrd + Sized { fn chmax(&mut self, mut rhs: Self) -> bool { if self < &mut rhs { *self = rhs; true } else { false } } fn chmin(&mut self, mut rhs: Self) -> bool { if self > &mut rhs { *self = rhs; true } else { false } } } impl MyOrd for T {} #[derive(Debug, Clone, Default)] pub struct BTreeMultiset { len: usize, set: BTreeMap } impl<'a, T: Ord> BTreeMultiset { pub fn new() -> Self { Self { len: 0, set: BTreeMap::new() } } pub fn len(&self) -> usize { self.len } pub fn count(&self, x: &T) -> usize { self.set.get(x).copied().unwrap_or(0) } pub fn insert_multiple(&mut self, x: T, count: usize) -> usize { self.len += count; let n = self.set.entry(x).or_insert(0); *n += count; *n } pub fn insert(&mut self, x: T) -> usize { self.insert_multiple(x, 1) } pub fn remove_multiple(&mut self, x: &T, count: usize) -> usize { if let Some(n) = self.set.get_mut(x) { let n0 = *n; *n = n0.saturating_sub(count); let n = *n; self.len -= n0 - n; if n == 0 { self.set.remove(x); } n } else { 0 } } pub fn remove(&mut self, x: &T) -> usize { self.remove_multiple(x, 1) } pub fn iter(&'a self) -> btree_map::Iter<'a, T, usize> { self.set.iter() } pub fn into_iter(self) -> btree_map::IntoIter { self.set.into_iter() } pub fn keys(&'a self) -> btree_map::Keys<'a, T, usize> { self.set.keys() } pub fn range(&'a self, range: impl RangeBounds) -> btree_map::Range<'a, T, usize> { self.set.range(range) } } type N = Int; pub struct LinearSieve { limit: usize, primes: Vec, table: Vec } impl LinearSieve { const REM: [usize; 8] = [1, 7, 11, 13, 17, 19, 23, 29]; const IDX: [usize; 30] = [8, 0, 8, 8, 8, 8, 8, 1, 8, 8, 8, 2, 8, 3, 8, 8, 8, 4, 8, 5, 8, 8, 8, 6, 8, 8, 8, 8, 8, 7]; pub fn new(limit: N) -> Self { let limit: usize = (limit); let mut table = vec![1; (limit + 29) / 30 * 8 + 1]; let mut primes = Vec::with_capacity(32); for i in 1 .. table.len() { let n = 30 * (i >> 3) + Self::REM[i & 7]; if table[i] == 1 { table[i] = n; primes.push(n); } for &p in &primes { if n * p > limit || p > table[i] { break; } table[n * p / 30 << 3 | Self::IDX[n * p % 30]] = p; } } Self { limit, table, primes } } pub fn is_prime(&self, n: N) -> bool { let n: usize = n; assert!(n <= self.limit); n == 2 || n == 3 || n == 5 || n % 2 != 0 && n % 3 != 0 && n % 5 != 0 && self.table[Self::index(n)] == n } pub fn primes(&self) -> Vec { self.primes.iter().map(|&n| (n) ).collect::>() } pub fn least_prime_factor(&self, n: N) -> N { let n: usize = (n); assert!(n <= self.limit); if n % 2 == 0 { return (2); } if n % 3 == 0 { return (3); } if n % 5 == 0 { return (5); } (self.table[Self::index(n)]) } pub fn prime_division(&self, mut n: N) -> Vec { assert!(n <= self.limit); let mut divisors = vec![]; while n > 1 { let d = self.least_prime_factor(n); n /= d; divisors.push(d); } divisors } pub fn prime_division_pairs(&self, n: N) -> Vec<(N, usize)> { if n == 1 { return vec![]; } let pd = self.prime_division(n); let mut prev_p = pd[0]; let mut e = 0; let mut pairs = vec![]; for p in pd.into_iter().chain(Some(1)) { if p == prev_p { e += 1; } else { pairs.push((prev_p, e)); prev_p = p; e = 1; } } pairs } fn index(n: usize) -> usize { n / 30 << 3 | Self::IDX[n % 30] } }