結果

問題 No.3399 One Two Three Two Three
コンテスト
ユーザー definieren
提出日時 2026-01-11 11:03:25
言語 C++23
(gcc 15.2.0 + boost 1.89.0)
結果
AC  
実行時間 1,961 ms / 4,000 ms
コード長 32,288 bytes
記録
記録タグの例:
初AC ショートコード 純ショートコード 純主流ショートコード 最速実行時間
コンパイル時間 5,402 ms
コンパイル使用メモリ 368,344 KB
実行使用メモリ 31,744 KB
最終ジャッジ日時 2026-01-11 11:03:51
合計ジャッジ時間 24,802 ms
ジャッジサーバーID
(参考情報)
judge4 / judge2
このコードへのチャレンジ
(要ログイン)
ファイルパターン 結果
sample AC * 5
other AC * 40
権限があれば一括ダウンロードができます

ソースコード

diff #
raw source code

#include <bits/stdc++.h>

using u32 = unsigned;
using i64 = long long;
using u64 = unsigned long long;
using i128 = __int128;
using u128 = unsigned __int128;

template<class U0, class U1, class S0, U0 P>
struct static_modint {
private:
	static_assert((P >> (sizeof(U0) * 8 - 1)) == 0, "'Mod' must less than max(U0)/2");
	static constexpr U0 Mod = P;
	U0 x;
	template<class T>
	static constexpr U0 safeMod(T x) {
		if constexpr (std::is_unsigned<T>::value) {
			return static_cast<U0>(x % Mod);
		} else {
			((x %= static_cast<S0>(Mod)) < 0) && (x += Mod);
			return static_cast<U0>(x);
		}
	}
public:
	constexpr static_modint(): x(static_cast<U0>(0)) {}
	template<class T>
	constexpr static_modint(T _x): x(safeMod(_x)) {}
	static constexpr static_modint from_raw(U0 _x) noexcept {
		static_modint x;
		return x.x = _x, x;
	}
	static constexpr U0 getMod() {
		return Mod;
	}
	template<class T>
	explicit constexpr operator T() const {
		return static_cast<T>(x);
	}
	constexpr static_modint &operator += (const static_modint &rhs) {
		x += rhs.x, (x - Mod) >> (sizeof(U0) * 8 - 1) || (x -= Mod);
		return *this;
	}
	constexpr static_modint &operator -= (const static_modint &rhs) {
		x -= rhs.x, x >> (sizeof(U0) * 8 - 1) && (x += Mod);
		return *this;
	}
	constexpr static_modint &operator *= (const static_modint &rhs) {
		x = static_cast<U0>(static_cast<U1>(x) * static_cast<U1>(rhs.x) % static_cast<U1>(Mod));
		return *this;
	}
	constexpr static_modint &operator /= (const static_modint &rhs) {
		return (*this *= rhs.inv());
	}
	friend constexpr static_modint fma(const static_modint &a, const static_modint &b, const static_modint &c) {
		return from_raw((static_cast<U1>(a.x) * b.x + c.x) % Mod);
	}
	friend constexpr static_modint fam(const static_modint &a, const static_modint &b, const static_modint &c) {
		return from_raw((a.x + static_cast<U1>(b.x) * c.x) % Mod);
	}
	friend constexpr static_modint fms(const static_modint &a, const static_modint &b, const static_modint &c) {
		return from_raw((static_cast<U1>(a.x) * b.x + Mod - c.x) % Mod);
	}
	friend constexpr static_modint fsm(const static_modint &a, const static_modint &b, const static_modint &c) {
		return from_raw((a.x + static_cast<U1>(Mod - b.x) * c.x) % Mod);
	}
	constexpr static_modint inv() const {
		U0 a = Mod, b = x; S0 y = 0, z = 1;
		while (b) {
			const U0 q = a / b;
			const U0 c = a - q * b;
			a = b, b = c;
			const S0 w = y - static_cast<S0>(q) * z;
			y = z, z = w;
		}
		return from_raw(y < 0 ? y + Mod : y);
	}
	friend constexpr static_modint inv(const static_modint &x) {
		return x.inv();
	}
	friend constexpr static_modint operator + (const static_modint &x) {
		return x;
	}
	friend constexpr static_modint operator - (static_modint x) {
		x.x = x.x ? (Mod - x.x) : 0U;
		return x;
	}
	constexpr static_modint &operator ++ () {
		return *this += 1;
	}
	constexpr static_modint &operator -- () {
		return *this -= 1;
	}
	constexpr static_modint operator ++ (int) {
		static_modint v = *this;
		return *this += 1, v;
	}
	constexpr static_modint operator -- (int) {
		static_modint v = *this;
		return *this -= 1, v;
	}
	friend constexpr static_modint operator + (static_modint x, const static_modint &y) {
		return x += y;
	}
	friend constexpr static_modint operator - (static_modint x, const static_modint &y) {
		return x -= y;
	}
	friend constexpr static_modint operator * (static_modint x, const static_modint &y) {
		return x *= y;
	}
	friend constexpr static_modint operator / (static_modint x, const static_modint &y) {
		return x /= y;
	}
	template<class T>
	constexpr static_modint pow(T y) const {
		if (y < 0) return inv().pow(- y);
		static_modint x = *this, ans = from_raw(1U);
		for (; y; y >>= 1, x *= x) {
			if (y & 1) {
				ans *= x;
			}
		}
		return ans;
	}
	template<class T>
	friend constexpr static_modint pow(const static_modint &x, T y) {
		return x.pow(y);
	}
	std::pair<bool, static_modint> sqrt() const {
		if (x == 0U) {
			return {true, from_raw(0)};
		}
		if (Mod == 2U) {
			return {true, from_raw(1)};
		}
		if (pow((Mod - 1) / 2) != from_raw(1)) {
			return {false, from_raw(0)};
		}
		static std::mt19937_64 rnd(std::chrono::system_clock::now().time_since_epoch().count());
		std::uniform_int_distribution<U0> uid(1U, Mod - 1);
		static_modint x, y;
		do {
			x = from_raw(uid(rnd));
			y = x * x - *this;
		} while (y.pow((Mod - 1) / 2) == from_raw(1));
		auto mul = [](std::pair<static_modint, static_modint> &f,
			const std::pair<static_modint, static_modint> &g, const static_modint &h) {
			f = {f.first * g.first + f.second * g.second * h,
					 f.first * g.second + f.second * g.first};
		};
		std::pair<static_modint, static_modint> f{x, 1}, g{1, 0};
		auto exp = (Mod + 1) / 2;
		for (; exp; exp >>= 1, mul(f, f, y)) {
			if (exp & 1) {
				mul(g, f, y);
			}
		}
		return {true, from_raw(std::min(g.first.x, Mod - g.first.x))};
	}
	friend std::pair<bool, static_modint> sqrt(const static_modint &x) {
		return x.sqrt();
	}
	friend constexpr std::istream& operator >> (std::istream& is, static_modint &x) {
		S0 y;
		is >> y, x = y;
		return is;
	}
	friend constexpr std::ostream& operator << (std::ostream& os, const static_modint &x) {
		return os << x.x;
	}
	friend constexpr bool operator == (const static_modint &x, const static_modint &y) {
		return x.x == y.x;
	}
	friend constexpr bool operator != (const static_modint &x, const static_modint &y) {
		return x.x != y.x;
	}
	friend constexpr bool operator <= (const static_modint &x, const static_modint &y) {
		return x.x <= y.x;
	}
	friend constexpr bool operator >= (const static_modint &x, const static_modint &y) {
		return x.x >= y.x;
	}
	friend constexpr bool operator < (const static_modint &x, const static_modint &y) {
		return x.x < y.x;
	}
	friend constexpr bool operator > (const static_modint &x, const static_modint &y) {
		return x.x > y.x;
	}
};
template<u32 P>
using sm32 = static_modint<u32, u64, int, P>;
template<u64 P>
using sm64 = static_modint<u64, u128, i64, P>;
using Z = sm32<998244353U>;

struct Combination {
	int N;
	std::vector<Z> _fac, _ifac, _inv;
	
	Combination(int n): N(0), _fac{1}, _ifac{1}, _inv{0} { init(n); }
	Combination(): N(0), _fac{1}, _ifac{1}, _inv{0} {}
	void init(int n) {
		if (n <= N) return;
		_fac.resize(n + 1), _ifac.resize(n + 1), _inv.resize(n + 1);
		for (int i = N + 1; i <= n; i ++) _fac[i] = _fac[i - 1] * i;
		_ifac[n] = _fac[n].inv();
		for (int i = n; i > N; i --) _ifac[i - 1] = _ifac[i] * i,
																 _inv[i] = _ifac[i] * _fac[i - 1];
		N = n; return;
	}
	Z fac(int n) {
		init(n << 1);
		return n < 0 ? Z::from_raw(0) : _fac[n];
	}
	Z ifac(int n) {
		init(n << 1);
		return n < 0 ? Z::from_raw(0) : _ifac[n];
	}
	Z inv(int n) {
		init(n << 1);
		return n < 0 ? Z::from_raw(0) : _inv[n];
	}
	Z binom(int n, int m) {
		if (n < m || n < 0 || m < 0) return 0;
		return fac(n) * ifac(m) * ifac(n - m);
	}
} comb;

template<class Z> 
struct polynomial: public std::vector<Z> {
//private:
	static constexpr Z g = 3;
	static constexpr auto Mod = Z::getMod();
	static constexpr int log_ord = []() {
		auto x = Mod - 1;
		int y = 0;
		while (~x & 1) {
			x >>= 1, ++ y;
		}
		return y;
	}();
	static constexpr std::array<Z, log_ord + 1> invn = []() {
		std::array<Z, log_ord + 1> inv{};
		for (int i = 0; i <= log_ord; i ++) {
			inv[i] = Z(1 << i).inv();
		}
		return inv;
	}();
	static std::pair<Z*, Z*> get_root(const int &n) {
		static std::vector<Z> root{Z::from_raw(1)};
		static std::vector<Z> inv_root{Z::from_raw(1)};
		if (static_cast<int>(root.size()) < n) {
			int i = root.size();
			root.resize(n), inv_root.resize(n);
			for (; i != n; i <<= 1) {
				const Z w = g.pow(Mod / (i << 2)), iw = w.inv();
				for (int j = 0; j != i; j ++) {
					root[i + j] = root[j] * w;
					inv_root[i + j] = inv_root[j] * iw;
				}
			}
		}
		return {root.data(), inv_root.data()};
	}
	static constexpr int get_len(int n) {
		return n < 3 ? n : 2 << std::__lg(n - 1);
	}
	static void dif_n(Z *f, const int &n) {
		const Z* rt = get_root(n).first;
		for (int i = n; i >>= 1; ) {
			for (int j = 0, k = 0; j != n; j += i << 1, ++ k) {
				for (int p = j, q = j + i; p != j + i; ++ p, ++ q) {
					const Z u = f[p], v = f[q] * rt[k];
					f[p] = u + v, f[q] = u - v;
				}
			}
		}
	}
	static void dit_n(Z *f, const int &n) {
		const Z* irt = get_root(n).second;
		for (int i = 1; i != n; i <<= 1) {
			for (int j = 0, k = 0; j != n; j += i << 1, ++ k) {
				for (int p = j, q = j + i; p != j + i; ++ p, ++ q) {
					const Z u = f[p], v = f[q];
					f[p] = u + v, f[q] = (u - v) * irt[k];
				}
			}
		}
		const Z inv = invn[std::__lg(n)];
		for (int i = 0; i < n; i ++) {
			f[i] *= inv;
		}
	}
	static void dif_rhalf_n(Z *f, const int &n) {
		const Z* rt = get_root(n).first;
		for (int i = n, m = 1; i >>= 1; m <<= 1) {
			for (int j = 0, k = 0; j != n; j += i << 1, ++ k) {
				for (int p = j, q = j + i; p != j + i; ++ p, ++ q) {
					const Z u = f[p], v = f[q] * rt[m + k];
					f[p] = u + v, f[q] = u - v;
				}
			}
		}
	}
	static void dit_rhalf_n(Z *f, const int &n) {
		const Z* irt = get_root(n).second;
		for (int i = 1, m = n; m >>= 1; i <<= 1) {
			for (int j = 0, k = 0; j != n; j += i << 1, ++ k) {
				for (int p = j, q = j + i; p != j + i; ++ p, ++ q) {
					const Z u = f[p], v = f[q];
					f[p] = u + v, f[q] = (u - v) * irt[m + k];
				}
			}
		}
		const Z inv = invn[std::__lg(n)];
		for (int i = 0; i < n; i ++) {
			f[i] *= inv;
		}
	}
	static void neg_n(const Z *a, const int &n, Z *b) {
		for (int i = 0; i < n; i ++) {
			b[i] = -a[i];
		}
	}
	static void add_n(const Z *a, const Z *b, const int &n, Z *c) {
		for (int i = 0; i < n; i ++) {
			c[i] = a[i] + b[i];
		}
	}
	static void sub_n(const Z *a, const Z *b, const int &n, Z *c) {
		for (int i = 0; i < n; i ++) {
			c[i] = a[i] - b[i];
		}
	}
	static void dot_n(const Z *a, const Z *b, const int &n, Z *c) {
		for (int i = 0; i < n; i ++) {
			c[i] = a[i] * b[i];
		}
	}
	static void mul_c_n(const Z *a, const Z &c, const int &n, Z *b) {
		for (int i = 0; i < n; i ++) {
			b[i] = a[i] * c;
		}
	}
	static void deriv_n(const Z *a, const int &n, Z *b) {
		for (int i = 1; i != n; i ++) {
			b[i - 1] = a[i] * i;
		}
		b[n - 1] = Z::from_raw(0);
	}
	static void integ_n(const Z *a, const int &n, Z *b) {
		comb.init(n);
		for (int i = n - 1; i; i --) {
			b[i] = a[i - 1] * comb.inv(i);
		}
		b[0] = Z::from_raw(0);
	}
	static void zero_n(Z *a, const int &n) {
		for (int i = 0; i < n; i ++) {
			a[i] = Z::from_raw(0);
		}
	}
	static void copy_n(const Z *a, const int &n, Z *b) {
		memcpy(b, a, sizeof(Z) * n);
	}
	static polynomial convolution_fft(polynomial f, polynomial g) {
		const int n = f.size() + g.size() - 1, m = get_len(n);
		f.resize(m), dif_n(f.data(), m);
		g.resize(m), dif_n(g.data(), m);
		dot_n(f.data(), g.data(), m, f.data());
		dit_n(f.data(), m), f.resize(n);
		return f;
	}
	static polynomial convolution_naive(const polynomial &f, const polynomial &g) {
		const int n = f.size(), m = g.size();
		if (__builtin_expect(f.empty() || g.empty(), 0)) {
			return polynomial{};
		}
		polynomial fg(n + m - 1);
		for (int i = 0; i != n; i ++) {
			for (int j = 0; j != m; j ++) {
				fg[i + j] += f[i] * g[j];
			}
		}
		return fg;
	}
	polynomial pow_one(const int &n, const int &k) const {
		if (__builtin_expect(k == 0, 0)) {
			polynomial f(this->size());
			f[0] = 1;
			return f;
		}
		return (k == 1) ? mod_xk(n) : (mod_xk(n).log() * k).exp();
	}
	polynomial pow_ord_zero(const int &n, int k_mod_p, int k_mod_phi_p) const {
		if ((*this)[0] == Z::from_raw(1)) {
			return pow_one(n, k_mod_p);
		}
		return (*this * (*this)[0].inv()).pow_one(n, k_mod_p) * (*this)[0].pow(k_mod_phi_p);
	}
	polynomial sqrt_ord_zero(const Z &s) const {
		constexpr Z c = -Z(2).inv();
		const int shrink_len = this->size();
		const int n = get_len(shrink_len);
		polynomial res(shrink_len), inv_res(shrink_len);
		polynomial dft_res(n), dft_inv_res(n), f(n);
		int N = 1, N2 = 2;
		res[0] = dft_res[0] = s, inv_res[0] = s.inv();
		while (N < shrink_len) {
			const int newN = (N2 == n) ? shrink_len : N2;
			dot_n(dft_res.data(), dft_res.data(), N, dft_res.data()), dit_n(dft_res.data(), N);
			sub_n(dft_res.data(), this->data(), N, dft_res.data() + N);
			sub_n(dft_res.data() + N, this->data() + N, newN - N, dft_res.data() + N);
			zero_n(dft_res.data(), N), dif_n(dft_res.data(), N2);
			copy_n(inv_res.data(), N, dft_inv_res.data()), dif_n(dft_inv_res.data(), N2);
			dot_n(dft_res.data(), dft_inv_res.data(), N2, dft_res.data()), dit_n(dft_res.data(), N2);
			mul_c_n(dft_res.data() + N, c, newN - N, res.data() + N);
			if (__builtin_expect(N2 < n, 1)) {
				copy_n(res.data(), N2, f.data()), dif_n(f.data(), N2);
				copy_n(f.data(), N2, dft_res.data());
				dot_n(f.data(), dft_inv_res.data(), N2, f.data()), dit_n(f.data(), N2);
				zero_n(f.data(), N), dif_n(f.data(), N2);
				dot_n(f.data(), dft_inv_res.data(), N2, f.data()), dit_n(f.data(), N2);
				neg_n(f.data() + N, N, inv_res.data() + N);
			}
			N <<= 1, N2 <<= 1;
		}
		return res;
	}
public:
	polynomial(): std::vector<Z>() {}
	explicit polynomial(const int &n): std::vector<Z>(n) {}
	explicit polynomial(const std::vector<Z> &a): std::vector<Z>(a) {}
	explicit polynomial(const std::initializer_list<Z> &a): std::vector<Z>(a) {}
	template<class _InputIterator, class = std::_RequireInputIter<_InputIterator>>
	explicit polynomial(_InputIterator __first, _InputIterator __last): std::vector<Z>(__first, __last) {}
	template<class F, class = Z(*)(int)>
	explicit polynomial(const int &n, const F &f): std::vector<Z>(n) {
		for (int i = 0; i != n; i ++) {
			(*this)[i] = f(i);
		}
	}
	
	int ord() const {
		int ord = 0;
		while (ord != static_cast<int>(this->size()) && !(*this)[ord]) {
			++ ord;
		}
		return ord;
	}
	int deg() const {
		int deg = static_cast<int>(this->size()) - 1;
		while (deg >= 0 && !(*this)[deg]) {
			-- deg;
		}
		return deg;
	}
	void remove0() {
		while (this->size() && !this->back()) {
			this->pop_back();
		}
	}
	polynomial mul_xk(const int &k) const {
		auto f = *this;
		f.insert(f.begin(), k, Z::from_raw(0));
		return f;
	}
	polynomial div_xk(const int &k) const {
		return polynomial(this->begin() + std::min(static_cast<int>(this->size()), k), this->end());
	}
	polynomial mod_xk(const int &k) const {
		return polynomial(this->begin(), this->begin() + std::min(static_cast<int>(this->size()), k));
	}
	polynomial compose_cx(const Z &c) const {
		const int n = this->size();
		auto f = *this;
		Z ci = Z::from_raw(1);
		for (int i = 1; i < n; i ++) {
			f[i] *= (ci *= c);
		}
		return f;
	}
	Z operator () (const Z &x) const {
		const int n = this->size();
		Z y = Z::from_raw(0);
		for (int i = n - 1; i >= 0; i --) {
			(y *= x) += (*this)[i];
		}
		return y;
	}
	polynomial operator + () const {
		return *this;
	}
	polynomial operator - () const {
		auto f = *this;
		neg_n(f.data(), f.size(), f.data());
		return f;
	}
	polynomial& operator += (const Z &rhs) {
		if (__builtin_expect(this->empty(), 0)) {
			return polynomial(1, rhs);
		}
		return (*this)[0] += rhs, *this;
	}
	polynomial& operator -= (const Z &rhs) {
		if (__builtin_expect(this->empty(), 0)) {
			return polynomial(1, -rhs);
		}
		return (*this)[0] -= rhs, *this;
	}
	polynomial& operator *= (const Z &rhs) {
		mul_c_n(this->data(), rhs, this->size(), this->data());
		return *this;
	}
	polynomial& operator /= (const Z &rhs) {
		mul_c_n(this->data(), rhs.inv(), this->size(), this->data());
		return *this;
	}
	polynomial& operator += (const polynomial &rhs) {
		if (this->size() < rhs.size()) {
			this->resize(rhs.size());
		}
		add_n(this->data(), rhs.data(), rhs.size(), this->data());
		return *this;
	}
	polynomial& operator -= (const polynomial &rhs) {
		if (this->size() < rhs.size()) {
			this->resize(rhs.size());
		}
		sub_n(this->data(), rhs.data(), rhs.size(), this->data());
		return *this;
	}
	polynomial& operator *= (const polynomial &rhs) {
		return *this = *this * rhs;
	}
	friend polynomial operator + (polynomial lhs, const Z &rhs) {
		return lhs += rhs;
	}
	friend polynomial operator - (polynomial lhs, const Z &rhs) {
		return lhs -= rhs;
	}
	friend polynomial operator * (polynomial lhs, const Z &rhs) {
		return lhs *= rhs;
	}
	friend polynomial operator / (polynomial lhs, const Z &rhs) {
		return lhs /= rhs;
	}
	friend polynomial operator + (polynomial lhs, const polynomial &rhs) {
		return lhs += rhs;
	}
	friend polynomial operator - (polynomial lhs, const polynomial &rhs) {
		return lhs -= rhs;
	}
	friend polynomial operator * (const polynomial &f, const polynomial &g) {
		if (std::min(f.size(), g.size()) > 8 && std::max(f.size(), g.size()) > 128) {
			return convolution_fft(f, g);
		} else {
			return convolution_naive(f, g);
		}
	}
	polynomial deriv() const {
		if (__builtin_expect(this->emtpy())) {
			return *this;
		}
		auto f = *this;
		deriv_n(f.data(), f.size(), f.data());
		return f.pop_back(), f;
	}
	polynomial integ() const {
		auto f = *this;
		f.resize(this->size() + 1);
		integ_n(f.data(), f.size(), f.data());
		return f.pop_back(), f;
	}
	polynomial inv() const {
		if (__builtin_expect(this->empty(), 0)) {
			return polynomial{};
		}
		assert((*this)[0] != Z::from_raw(0));
		const int shrink_len = this->size();
		const int n = get_len(shrink_len);
		polynomial res(shrink_len), f(n), g(n);
		res[0] = (*this)[0].inv();
		int N = 1, N2 = 2;
		while (N < shrink_len) {
			const int newN = (N2 == n) ? shrink_len : N2;
			copy_n(this->data(), newN, f.data()), dif_n(f.data(), N2);
			copy_n(res.data(), N, g.data()), dif_n(g.data(), N2);
			dot_n(f.data(), g.data(), N2, f.data()), dit_n(f.data(), N2);
			zero_n(f.data(), N), dif_n(f.data(), N2);
			dot_n(f.data(), g.data(), N2, f.data()), dit_n(f.data(), N2);
			neg_n(f.data() + N, newN - N, res.data() + N);
			N = newN, N2 = N << 1;
		}
		return res;
	}
	polynomial div(const polynomial &g) const {
		if (g.size() > this->size()) {
			return polynomial{};
		}
		assert(g.size());
		const int n = this->size() - g.size() + 1;
		polynomial q(n), r(n);
		std::reverse_copy(this->begin() + g.size() - 1, this->end(), q.begin());
		std::reverse_copy(g.begin() + std::max(0, static_cast<int>(g.size()) - n), g.end(), r.begin());
		q = (q * r.inv()).mod_xk(n), std::reverse(q.begin(), q.end());
		return q;
	}
	std::pair<polynomial, polynomial> div_mod(const polynomial &g) const {
		if (g.size() > this->size()) {
			return {polynomial{}, *this};
		}
		assert(g.size());
		const int n = g.size() - 1;
		auto q = div(g);
		return {q, mod_xk(n) - (g.mod_xk(n) * q.mod_xk(n)).mod_xk(n)};
	}
	polynomial exp() const {
		if (__builtin_expect(this->empty(), 0)) {
			return polynomial{};
		}
		assert((*this)[0] == Z::from_raw(0));
		if (__builtin_expect(this->size() == 1, 0)) {
			return polynomial{Z::from_raw(1)};
		}
		const int shrink_len = this->size();
		const int n = get_len(shrink_len);
		int N = 1, N2 = 2;
		polynomial res(shrink_len), inv_res(shrink_len);
		polynomial dft_res(n), dft_inv_res(n), f(n);
		res[0] = inv_res[0] = dft_res[0] = dft_res[1] =1;
		while (N < shrink_len) {
			const int newN = (N2 == n) ? shrink_len : N2;
			deriv_n(this->data(), N, f.data()), dif_n(f.data(), N);
			dot_n(f.data(), dft_res.data(), N, f.data()), dit_n(f.data(), N);
			f[N - 1] = -f[N - 1], deriv_n(res.data(), N, f.data() + N);
			sub_n(f.data() + N, f.data(), N - 1, f.data() + N);
			zero_n(f.data(), N - 1), dif_n(f.data(), N2);
			copy_n(inv_res.data(), N, dft_inv_res.data()), dif_n(dft_inv_res.data(), N2);
			dot_n(f.data(), dft_inv_res.data(), N2, f.data()), dit_n(f.data(), N2);
			integ_n(f.data(), N2, f.data()), zero_n(f.data(), N);
			sub_n(f.data() + N, this->data() + N, newN - N, f.data() + N), dif_n(f.data(), N2);
			dot_n(f.data(), dft_res.data(), N2, f.data()), dit_n(f.data(), N2);
			neg_n(f.data() + N, newN - N, res.data() + N);
			if (__builtin_expect(N2 < n, 1)) {
				copy_n(res.data(), N2, dft_res.data()), dif_n(dft_res.data(), N2 + N2);
				copy_n(dft_res.data(), N2, f.data());
				dot_n(f.data(), dft_inv_res.data(), N2, f.data()), dit_n(f.data(), N2);
				zero_n(f.data(), N), dif_n(f.data(), N2);
				dot_n(f.data(), dft_inv_res.data(), N2, f.data()), dit_n(f.data(), N2);
				neg_n(f.data() + N, N, inv_res.data() + N);
			}
			N <<= 1, N2 <<= 1;
		}
		return res;
	}
	polynomial log() const {
		if (__builtin_expect(this->empty(), 0)) {
			return polynomial{};
		}
		assert((*this)[0] == Z::from_raw(1));
		polynomial f(this->size());
		deriv_n(this->data(), this->size(), f.data());
		f *= inv(), f.resize(this->size());
		integ_n(f.data(), this->size(), f.data());
		return f;
	}
	polynomial pow(int k_chkmin_sz, int k_mod_p, int k_mod_phi_p) const {
		if (__builtin_expect(this->empty(), 0)) {
			return polynomial{};
		}
		if (__builtin_expect(k_chkmin_sz == 0, 0)) {
			polynomial f(this->size());
			return f[0] = Z::from_raw(1), f;
		}
		const int i = ord();
		if (static_cast<i64>(i) * k_chkmin_sz >= static_cast<i64>(this->size())) {
			return polynomial(this->size());
		}
		return div_xk(i).pow_ord_zero(this->size() - i * k_chkmin_sz, k_mod_p, k_mod_phi_p).mul_xk(i * k_chkmin_sz);
	}
	template<class T> inline polynomial pow(T x) const {
		if (x < 0) return inv().pow(-x);
		return pow(x < static_cast<T>(this->size()) ? x : this->size(), x % Mod, x % (Mod - 1));
	}
	std::pair<bool, polynomial> sqrt() const {
		const int i = ord();
		if (i == static_cast<int>(this->size())) {
			return {true, polynomial(this->size())};
		}
		if (i & 1) {
			return {false, polynomial{}};
		}
		auto [o, s] = (*this)[i].sqrt();
		if (o == false) {
			return {false, polynomial{}};
		}
		auto x = div_xk(i);
		x.insert(x.end(), i >> 1, 0);
		return {true, x.sqrt_ord_zero(s).mul_xk(i >> 1)};
	}
	polynomial composional_inv() const {
		assert(this->size() >= 2U);
		assert((*this)[0] == Z::from_raw(0));
		assert((*this)[1] != Z::from_raw(0));
		constexpr Z inv2 = Z(2).inv();
		const int shrink_len = this->size();
		const int n = get_len(shrink_len << 1);
		const Z *irt = get_root(2 * n).second;
		const Z invf1 = (*this)[1].inv();
		
		polynomial P(n), Q(n);
		polynomial dftP(n << 1), dftQ(n << 1);
		P[0] = Z::from_raw(1);
		Z v = -1;
		for (int i = 1; i < shrink_len; i ++) {
			v *= invf1, Q[i] = (*this)[i] * v;
		}
		int N = shrink_len, M = 1, newN = 0;
		for (; N > 1; N = newN, M <<= 1) {
			newN = (N + 1) >> 1;
			const int len = get_len((N * M) << 2);
			zero_n(dftP.data(), len), zero_n(dftQ.data(), len);
			for (int j = 0; j < M; j ++) {
				copy_n(P.data() + j * N, N, dftP.data() + 2 * j * N);
				copy_n(Q.data() + j * N, N, dftQ.data() + 2 * j * N);
			}
			
			dftQ[2 * N * M] = 1;
			dif_n(dftP.data(), len);
			dif_n(dftQ.data(), len);
			P.resize(len >> 1), Q.resize(len >> 1);
			for (int i = 0; i < len; i += 2) {
				Q[i >> 1] = dftQ[i] * dftQ[i + 1];
			}
			if (N & 1) {
				for (int i = 0; i < len; i += 2) {
					P[i >> 1] = (dftP[i] * dftQ[i + 1] + dftP[i + 1] * dftQ[i]) * inv2;
				}
			} else {
				for (int i = 0; i < len; i += 2) {
					P[i >> 1] = (dftP[i] * dftQ[i + 1] - dftP[i + 1] * dftQ[i]) * irt[i >> 1] * inv2;
				}
			}
			dit_n(P.data(), len >> 1);
			dit_n(Q.data(), len >> 1);
			if (N * M * 4 == len) {
				-- Q[0];
			}
			
			for (int j = 1; j < M * 2; j ++) {
				copy_n(P.data() + j * N, newN, P.data() + j * newN);
				copy_n(Q.data() + j * N, newN, Q.data() + j * newN);
			}
		}
		
		P.resize(M * newN);
		std::reverse(P.begin(), P.end());
		P.resize(shrink_len);
		for (int i = 1; i < shrink_len; i ++) {
			P[i] *= (shrink_len - 1) * comb.inv(i);
		}
		std::reverse(P.begin(), P.end());
		P = P.pow_one(shrink_len - 1, (int)(-comb.inv(shrink_len - 1))) * invf1;
		P.insert(P.begin(), 0);
		return P;
	}
	template<class T>
	friend Z linear_recurrence(polynomial f, polynomial g, T k) {
		constexpr Z inv2 = Z(2).inv();
		const int n = g.size();
		const int N = get_len(2 * n);
		const Z* irt = get_root(N).second;
		polynomial P(N), Q(N), dft(N);
		copy_n(f.data(), f.size(), P.data());
		copy_n(g.data(), n, Q.data());
		dif_n(P.data(), N);
		dif_n(Q.data(), N);
		while (k) {
			if (k & 1) {
				for (int i = 0; i < N / 2; i ++) {
					dft[i] = (P[2 * i] * Q[2 * i + 1] - P[2 * i + 1] * Q[2 * i]) * irt[i] * inv2;
				}
			} else {
				for (int i = 0; i < N / 2; i ++) {
					dft[i] = (P[2 * i] * Q[2 * i + 1] + P[2 * i + 1] * Q[2 * i]) * inv2;
				}
			}
			copy_n(dft.data(), N / 2, P.data());
			dit_n(dft.data(), N / 2);
			dif_rhalf_n(dft.data(), N / 2);
			copy_n(dft.data(), N / 2, P.data() + N / 2);
			for (int i = 0; i < N / 2; i ++) {
				dft[i] = Q[2 * i] * Q[2 * i + 1];
			}
			copy_n(dft.data(), N / 2, Q.data());
			dit_n(dft.data(), N / 2);
			dif_rhalf_n(dft.data(), N / 2);
			copy_n(dft.data(), N / 2, Q.data() + N / 2);
			k >>= 1;
		}
		dit_n(P.data(), N);
		dit_n(Q.data(), N);
		return P[0] / Q[0];
	}
};
using poly = polynomial<Z>;

constexpr int K = 62;
constexpr Z inv2 = Z(2).inv();

int main() {
#ifdef LOCAL
	freopen("!in.in", "r", stdin);
	freopen("!out.out", "w", stdout);
#endif
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);
	std::cout.tie(nullptr);

	i64 N;
	std::cin >> N;
	
	std::array<int, 5> op{};
	op.fill(1);
//	for (int o = 0; o < 5; o ++) {
//		std::cin >> op[o];
//	}
	
	std::array<poly, K> Q{}, Q2{}, Q3{};
	Q.fill(poly{1});
	
	auto get2 = [&](const poly &P, const poly& Q, int o) {
		static poly p, A[2], B[2];
		A[0].clear(), A[1].clear();
		for (int i = 0; i < (int)Q.size(); i ++) {
			A[i % 2].push_back(Q[i]);
		}
		B[0].clear(), B[1].clear();
		for (int i = 0; i < (int)P.size(); i ++) {
			B[i % 2].push_back(P[i]);
		}
		if (o == 0) {
			const int n = (P.size() + Q.size()) / 2;
			const int N = poly::get_len(n);
			const Z* rt = poly::get_root(N).first;
			A[0].resize(N);
			A[1].resize(N);
			B[0].resize(N);
			B[1].resize(N);
			poly::dif_n(A[0].data(), N);
			poly::dif_n(A[1].data(), N);
			poly::dif_n(B[0].data(), N);
			poly::dif_n(B[1].data(), N);
			p.resize(N);
			for (int i = 0; i < N; i ++) {
				if (i % 2 == 0) {
					p[i] = A[0][i] * B[0][i] - A[1][i] * B[1][i] * rt[i / 2];
				} else {
					p[i] = A[0][i] * B[0][i] + A[1][i] * B[1][i] * rt[i / 2];
				}
			}
			poly::dit_n(p.data(), N);
			p.resize(n);
		} else {
			const int n = (P.size() + Q.size() - 1) / 2;
			const int N = poly::get_len(n);
			A[0].resize(N);
			A[1].resize(N);
			B[0].resize(N);
			B[1].resize(N);
			poly::dif_n(A[0].data(), N);
			poly::dif_n(A[1].data(), N);
			poly::dif_n(B[0].data(), N);
			poly::dif_n(B[1].data(), N);
			p.resize(N);
			for (int i = 0; i < N; i ++) {
				p[i] = A[0][i] * B[1][i] - A[1][i] * B[0][i];
			}
			poly::dit_n(p.data(), N);
			p.resize(n);
		}
		return p;
	};
	auto get3 = [&](const poly &P, const poly& Q, int o) {
		static poly p, A[3], B[3];
		A[0].clear(), A[1].clear(), A[2].clear();
		for (int i = 0; i < (int)Q.size(); i ++) {
			A[i % 3].push_back(Q[i]);
		}
		B[0].clear(), B[1].clear(), B[2].clear();
		for (int i = 0; i < (int)P.size(); i ++) {
			B[i % 3].push_back(P[i]);
		}
		
		if (o == 0) {
			const int n = (P.size() + 2 * Q.size()) / 3;
			const int N = poly::get_len(n);
			const Z* rt = poly::get_root(N).first;
			A[0].resize(N);
			A[1].resize(N);
			A[2].resize(N);
			B[0].resize(N);
			B[1].resize(N);
			B[2].resize(N);
			poly::dif_n(A[0].data(), N);
			poly::dif_n(A[1].data(), N);
			poly::dif_n(A[2].data(), N);
			poly::dif_n(B[0].data(), N);
			poly::dif_n(B[1].data(), N);
			poly::dif_n(B[2].data(), N);
			p.resize(N);
			for (int i = 0; i < N; i ++) {
				p[i] = 0;
				if (i % 2 == 0) {
					p[i] += (A[0][i] * A[0][i] - A[1][i] * A[2][i] * rt[i / 2]) * B[0][i];
					p[i] += (A[2][i] * A[2][i] * rt[i / 2] - A[0][i] * A[1][i]) * B[2][i] * rt[i / 2];
					p[i] += (A[1][i] * A[1][i] - A[0][i] * A[2][i]) * B[1][i] * rt[i / 2];
				} else {
					p[i] += (A[0][i] * A[0][i] + A[1][i] * A[2][i] * rt[i / 2]) * B[0][i];
					p[i] += (A[2][i] * A[2][i] * rt[i / 2] + A[0][i] * A[1][i]) * B[2][i] * rt[i / 2];
					p[i] += (A[0][i] * A[2][i] - A[1][i] * A[1][i]) * B[1][i] * rt[i / 2];
				}
			}
			poly::dit_n(p.data(), N);
			p.resize(n);
		} else if (o == 1) {
			const int n = (P.size() + 2 * Q.size() - 1) / 3;
			const int N = poly::get_len(n);
			const Z* rt = poly::get_root(N).first;
			A[0].resize(N);
			A[1].resize(N);
			A[2].resize(N);
			B[0].resize(N);
			B[1].resize(N);
			B[2].resize(N);
			poly::dif_n(A[0].data(), N);
			poly::dif_n(A[1].data(), N);
			poly::dif_n(A[2].data(), N);
			poly::dif_n(B[0].data(), N);
			poly::dif_n(B[1].data(), N);
			poly::dif_n(B[2].data(), N);
			p.resize(N);
			for (int i = 0; i < N; i ++) {
				p[i] = 0;
				if (i % 2 == 0) {
					p[i] += (A[0][i] * A[0][i] - A[1][i] * A[2][i] * rt[i / 2]) * B[1][i];
					p[i] += (A[2][i] * A[2][i] * rt[i / 2] - A[0][i] * A[1][i]) * B[0][i];
					p[i] += (A[1][i] * A[1][i] - A[0][i] * A[2][i]) * B[2][i] * rt[i / 2];
				} else {
					p[i] += (A[0][i] * A[0][i] + A[1][i] * A[2][i] * rt[i / 2]) * B[1][i];
					p[i] -= (A[2][i] * A[2][i] * rt[i / 2] + A[0][i] * A[1][i]) * B[0][i];
					p[i] += (A[0][i] * A[2][i] - A[1][i] * A[1][i]) * B[2][i] * rt[i / 2];
				}
			}
			poly::dit_n(p.data(), N);
			p.resize(n);
		} else {
			const int n = (P.size() + 2 * Q.size() - 2) / 3;
			const int N = poly::get_len(n);
			const Z* rt = poly::get_root(N).first;
			A[0].resize(N);
			A[1].resize(N);
			A[2].resize(N);
			B[0].resize(N);
			B[1].resize(N);
			B[2].resize(N);
			poly::dif_n(A[0].data(), N);
			poly::dif_n(A[1].data(), N);
			poly::dif_n(A[2].data(), N);
			poly::dif_n(B[0].data(), N);
			poly::dif_n(B[1].data(), N);
			poly::dif_n(B[2].data(), N);
			p.resize(N);
			for (int i = 0; i < N; i ++) {
				p[i] = 0;
				if (i % 2 == 0) {
					p[i] += (A[0][i] * A[0][i] - A[1][i] * A[2][i] * rt[i / 2]) * B[2][i];
					p[i] += (A[2][i] * A[2][i] * rt[i / 2] - A[0][i] * A[1][i]) * B[1][i];
					p[i] += (A[1][i] * A[1][i] - A[0][i] * A[2][i]) * B[0][i];
				} else {
					p[i] += (A[0][i] * A[0][i] + A[1][i] * A[2][i] * rt[i / 2]) * B[2][i];
					p[i] -= (A[2][i] * A[2][i] * rt[i / 2] + A[0][i] * A[1][i]) * B[1][i];
					p[i] += (A[1][i] * A[1][i] - A[0][i] * A[2][i]) * B[0][i];
				}
			}
			poly::dit_n(p.data(), N);
			p.resize(n);
		}
		return p;
	};
	
	auto getQ = [&](auto&& self, int i, int j, const poly& P) -> void {
		if (i + j == K) {
			return;
		}
		
		Q[i + j] *= P;
		if (i == 0) {
			Q2[i + j] = P;
		}
		if (j == 0) {
			Q3[i + j] = P;
		}
		
		if (!j) {
			self(self, i + 1, j, get2(P, P, 0));
		}
		
		self(self, i, j + 1, get3(P, P, 0));
	};
	getQ(getQ, 0, 0, poly{1, -op[0], -op[1], -op[2]});
	
	Q2[0] = poly{1};
	Q3[0] = poly{1};
	for (int i = 1; i < K; i ++) {
		Q[i] *= Q[i - 1];
		Q2[i] *= Q2[i - 1];
		Q3[i] *= Q3[i - 1];
	}
	
	auto trans2 = [&](int k, i64 N, const poly& P) -> std::pair<i64, poly> {
		return {N / 2, get2(P, Q[k], N % 2) * Q2[k + 1]};
	};
	auto trans2C = [&](int k, i64 N, const poly& P) -> std::pair<i64, poly> {
		return {N / 2, get2(P, Q[k], N % 2) * Q2[k + 1] * Q[0]};
	};
	auto trans3 = [&](int k, i64 N, const poly& P) -> std::pair<i64, poly> {
		return {N / 3, get3(P, Q[k], N % 3) * Q3[k + 1]};
	};
	auto solve = [&](auto&& self, int k, const std::vector<std::pair<i64, poly>>& Fs, const std::vector<std::pair<i64, poly>>& Gs) -> Z {
		if (Fs.empty() && Gs.empty()) {
			return 0;
		}
		
		assert(k < K);
		std::vector<std::pair<i64, poly>> fs, gs;
		Z ans = 0;
		
		for (auto [N, P] : Fs) {
			if (N == 0) {
				continue;
			}
			
			gs.push_back(trans2C(k, N, P.mul_xk(1)));
			if (op[3]) {
				fs.push_back(trans2(k, N, P));
			}
			if (op[4]) {
				fs.push_back(trans3(k, N, P));
			}
		}
		
		for (auto [N, P] : Gs) {
			if (N == 0) {
				ans += P[0] / Q[k][0];
				continue;
			}
			gs.push_back(trans2C(k, N, P));
		}
		
		auto reduce = [&](std::vector<std::pair<i64, poly>>& Ps) {
			std::sort(Ps.begin(), Ps.end(), [&](const std::pair<i64, poly>& x, const std::pair<i64, poly>& y) {
				return x.first < y.first;
			});
			std::vector<std::pair<i64, poly>> ps;
			for (int i = 0; i < (int)Ps.size(); i ++) {
				if (ps.empty() || ps.back().first != Ps[i].first) {
					ps.push_back(Ps[i]);
				} else {
					ps.back().second += Ps[i].second;
				}
			}
			Ps = std::move(ps);
		};
		
		reduce(fs);
		reduce(gs);
		ans += self(self, k + 1, fs, gs);
		
		return ans;
	};
	
	std::cout << solve(solve, 0, {{N, poly{1}}}, {}) << '\n';

	return 0;
}
0