// TLE tasukete /** * date : 2025-12-23 01:22:11 * author : Nyaan */ #define NDEBUG using namespace std; // intrinstic #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // utility namespace Nyaan { using ll = long long; using i64 = long long; using u64 = unsigned long long; using i128 = __int128_t; using u128 = __uint128_t; template using V = vector; template using VV = vector>; using vi = vector; using vl = vector; using vd = V; using vs = V; using vvi = vector>; using vvl = vector>; template using minpq = priority_queue, greater>; template struct P : pair { template constexpr P(Args... args) : pair(args...) {} using pair::first; using pair::second; P &operator+=(const P &r) { first += r.first; second += r.second; return *this; } P &operator-=(const P &r) { first -= r.first; second -= r.second; return *this; } P &operator*=(const P &r) { first *= r.first; second *= r.second; return *this; } template P &operator*=(const S &r) { first *= r, second *= r; return *this; } P operator+(const P &r) const { return P(*this) += r; } P operator-(const P &r) const { return P(*this) -= r; } P operator*(const P &r) const { return P(*this) *= r; } template P operator*(const S &r) const { return P(*this) *= r; } P operator-() const { return P{-first, -second}; } }; using pl = P; using pi = P; using vp = V; constexpr int inf = 1001001001; constexpr long long infLL = 4004004004004004004LL; template int sz(const T &t) { return t.size(); } template inline bool amin(T &x, U y) { return (y < x) ? (x = y, true) : false; } template inline bool amax(T &x, U y) { return (x < y) ? (x = y, true) : false; } template inline T Max(const vector &v) { return *max_element(begin(v), end(v)); } template inline T Min(const vector &v) { return *min_element(begin(v), end(v)); } template inline long long Sum(const vector &v) { return accumulate(begin(v), end(v), 0LL); } template int lb(const vector &v, const T &a) { return lower_bound(begin(v), end(v), a) - begin(v); } template int ub(const vector &v, const T &a) { return upper_bound(begin(v), end(v), a) - begin(v); } constexpr long long TEN(int n) { long long ret = 1, x = 10; for (; n; x *= x, n >>= 1) ret *= (n & 1 ? x : 1); return ret; } template pair mkp(const T &t, const U &u) { return make_pair(t, u); } template vector mkrui(const vector &v, bool rev = false) { vector ret(v.size() + 1); if (rev) { for (int i = int(v.size()) - 1; i >= 0; i--) ret[i] = v[i] + ret[i + 1]; } else { for (int i = 0; i < int(v.size()); i++) ret[i + 1] = ret[i] + v[i]; } return ret; }; template vector mkuni(const vector &v) { vector ret(v); sort(ret.begin(), ret.end()); ret.erase(unique(ret.begin(), ret.end()), ret.end()); return ret; } template vector mkord(int N, F f) { vector ord(N); iota(begin(ord), end(ord), 0); sort(begin(ord), end(ord), f); return ord; } template vector mkinv(vector &v) { int max_val = *max_element(begin(v), end(v)); vector inv(max_val + 1, -1); for (int i = 0; i < (int)v.size(); i++) inv[v[i]] = i; return inv; } vector mkiota(int n) { vector ret(n); iota(begin(ret), end(ret), 0); return ret; } template T mkrev(const T &v) { T w{v}; reverse(begin(w), end(w)); return w; } template bool nxp(T &v) { return next_permutation(begin(v), end(v)); } // 返り値の型は入力の T に依存 // i 要素目 : [0, a[i]) template vector> product(const vector &a) { vector> ret; vector v; auto dfs = [&](auto rc, int i) -> void { if (i == (int)a.size()) { ret.push_back(v); return; } for (int j = 0; j < a[i]; j++) v.push_back(j), rc(rc, i + 1), v.pop_back(); }; dfs(dfs, 0); return ret; } // F : function(void(T&)), mod を取る操作 // T : 整数型のときはオーバーフローに注意する template T Power(T a, long long n, const T &I, const function &f) { T res = I; for (; n; f(a = a * a), n >>= 1) { if (n & 1) f(res = res * a); } return res; } // T : 整数型のときはオーバーフローに注意する template T Power(T a, long long n, const T &I = T{1}) { return Power(a, n, I, function{[](T &) -> void {}}); } template T Rev(const T &v) { T res = v; reverse(begin(res), end(res)); return res; } template vector Transpose(const vector &v) { using U = typename T::value_type; if(v.empty()) return {}; int H = v.size(), W = v[0].size(); vector res(W, T(H, U{})); for (int i = 0; i < H; i++) { for (int j = 0; j < W; j++) { res[j][i] = v[i][j]; } } return res; } template vector Rotate(const vector &v, int clockwise = true) { using U = typename T::value_type; int H = v.size(), W = v[0].size(); vector res(W, T(H, U{})); for (int i = 0; i < H; i++) { for (int j = 0; j < W; j++) { if (clockwise) { res[W - 1 - j][i] = v[i][j]; } else { res[j][H - 1 - i] = v[i][j]; } } } return res; } } // namespace Nyaan // bit operation namespace Nyaan { __attribute__((target("popcnt"))) inline int popcnt(const u64 &a) { return __builtin_popcountll(a); } inline int lsb(const u64 &a) { return a ? __builtin_ctzll(a) : 64; } inline int ctz(const u64 &a) { return a ? __builtin_ctzll(a) : 64; } inline int msb(const u64 &a) { return a ? 63 - __builtin_clzll(a) : -1; } template inline int gbit(const T &a, int i) { return (a >> i) & 1; } template inline void sbit(T &a, int i, bool b) { if (gbit(a, i) != b) a ^= T(1) << i; } constexpr long long PW(int n) { return 1LL << n; } constexpr long long MSK(int n) { return (1LL << n) - 1; } } // namespace Nyaan // inout namespace Nyaan { template ostream &operator<<(ostream &os, const pair &p) { os << p.first << " " << p.second; return os; } template istream &operator>>(istream &is, pair &p) { is >> p.first >> p.second; return is; } template ostream &operator<<(ostream &os, const vector &v) { int s = (int)v.size(); for (int i = 0; i < s; i++) os << (i ? " " : "") << v[i]; return os; } template istream &operator>>(istream &is, vector &v) { for (auto &x : v) is >> x; return is; } istream &operator>>(istream &is, __int128_t &x) { string S; is >> S; x = 0; int flag = 0; for (auto &c : S) { if (c == '-') { flag = true; continue; } x *= 10; x += c - '0'; } if (flag) x = -x; return is; } istream &operator>>(istream &is, __uint128_t &x) { string S; is >> S; x = 0; for (auto &c : S) { x *= 10; x += c - '0'; } return is; } ostream &operator<<(ostream &os, __int128_t x) { if (x == 0) return os << 0; if (x < 0) os << '-', x = -x; string S; while (x) S.push_back('0' + x % 10), x /= 10; reverse(begin(S), end(S)); return os << S; } ostream &operator<<(ostream &os, __uint128_t x) { if (x == 0) return os << 0; string S; while (x) S.push_back('0' + x % 10), x /= 10; reverse(begin(S), end(S)); return os << S; } void in() {} template void in(T &t, U &...u) { cin >> t; in(u...); } void out() { cout << "\n"; } template void out(const T &t, const U &...u) { cout << t; if (sizeof...(u)) cout << sep; out(u...); } struct IoSetupNya { IoSetupNya() { cin.tie(nullptr); ios::sync_with_stdio(false); cout << fixed << setprecision(15); cerr << fixed << setprecision(7); } } iosetupnya; } // namespace Nyaan // debug #ifdef NyaanDebug #define trc(...) (void(0)) #else #define trc(...) (void(0)) #endif #ifdef NyaanLocal #define trc2(...) (void(0)) #else #define trc2(...) (void(0)) #endif // macro #define each(x, v) for (auto&& x : v) #define each2(x, y, v) for (auto&& [x, y] : v) #define all(v) (v).begin(), (v).end() #define rep(i, N) for (long long i = 0; i < (long long)(N); i++) #define repr(i, N) for (long long i = (long long)(N)-1; i >= 0; i--) #define rep1(i, N) for (long long i = 1; i <= (long long)(N); i++) #define repr1(i, N) for (long long i = (N); (long long)(i) > 0; i--) #define reg(i, a, b) for (long long i = (a); i < (b); i++) #define regr(i, a, b) for (long long i = (b)-1; i >= (a); i--) #define fi first #define se second #define ini(...) \ int __VA_ARGS__; \ in(__VA_ARGS__) #define inl(...) \ long long __VA_ARGS__; \ in(__VA_ARGS__) #define ins(...) \ string __VA_ARGS__; \ in(__VA_ARGS__) #define in2(s, t) \ for (int i = 0; i < (int)s.size(); i++) { \ in(s[i], t[i]); \ } #define in3(s, t, u) \ for (int i = 0; i < (int)s.size(); i++) { \ in(s[i], t[i], u[i]); \ } #define in4(s, t, u, v) \ for (int i = 0; i < (int)s.size(); i++) { \ in(s[i], t[i], u[i], v[i]); \ } #define die(...) \ do { \ Nyaan::out(__VA_ARGS__); \ return; \ } while (0) namespace Nyaan { void solve(); } int main() { Nyaan::solve(); } // using namespace std; using namespace std; // x / y (x > 0, y > 0) を管理、デフォルトで 1 / 1 // 入力が互いに素でない場合は gcd を取って格納 // seq : (1, 1) から (x, y) へのパス。右の子が正/左の子が負 template struct SternBrocotTreeNode { using Node = SternBrocotTreeNode; Int lx, ly, x, y, rx, ry; vector seq; SternBrocotTreeNode() : lx(0), ly(1), x(1), y(1), rx(1), ry(0) {} SternBrocotTreeNode(Int X, Int Y) : SternBrocotTreeNode() { assert(1 <= X && 1 <= Y); Int g = gcd(X, Y); X /= g, Y /= g; while (min(X, Y) > 0) { if (X > Y) { Int d = X / Y; X -= d * Y; go_right(d - (X == 0 ? 1 : 0)); } else { Int d = Y / X; Y -= d * X; go_left(d - (Y == 0 ? 1 : 0)); } } } SternBrocotTreeNode(const pair &xy) : SternBrocotTreeNode(xy.first, xy.second) {} SternBrocotTreeNode(const vector &_seq) : SternBrocotTreeNode() { for (const Int &d : _seq) { assert(d != 0); if (d > 0) go_right(d); if (d < 0) go_left(d); } assert(seq == _seq); } // pair 型で分数を get pair get() const { return make_pair(x, y); } // 区間の下限 pair lower_bound() const { return make_pair(lx, ly); } // 区間の上限 pair upper_bound() const { return make_pair(rx, ry); } // 根からの深さ Int depth() const { Int res = 0; for (auto &s : seq) res += abs(s); return res; } // 左の子に d 進む void go_left(Int d = 1) { if (d <= 0) return; if (seq.empty() or seq.back() > 0) seq.push_back(0); seq.back() -= d; rx += lx * d, ry += ly * d; x = rx + lx, y = ry + ly; } // 右の子に d 進む void go_right(Int d = 1) { if (d <= 0) return; if (seq.empty() or seq.back() < 0) seq.push_back(0); seq.back() += d; lx += rx * d, ly += ry * d; x = rx + lx, y = ry + ly; } // 親の方向に d 進む // d 進めたら true, 進めなかったら false を返す bool go_parent(Int d = 1) { if (d <= 0) return true; while (d != 0) { if (seq.empty()) return false; Int d2 = min(d, seq.back() < 0 ? -seq.back() : seq.back()); if (seq.back() > 0) { x -= rx * d2, y -= ry * d2; lx = x - rx, ly = y - ry; seq.back() -= d2; } else { x -= lx * d2, y -= ly * d2; rx = x - lx, ry = y - ly; seq.back() += d2; } d -= d2; if (seq.back() == 0) seq.pop_back(); if (d2 == Int{0}) break; } return true; } // SBT 上の LCA static Node lca(const Node &lhs, const Node &rhs) { Node n; for (int i = 0; i < min(lhs.seq.size(), rhs.seq.size()); i++) { Int val1 = lhs.seq[i], val2 = rhs.seq[i]; if ((val1 < 0) != (val2 < 0)) break; if (val1 < 0) n.go_left(min(-val1, -val2)); if (val1 > 0) n.go_right(min(val1, val2)); if (val1 != val2) break; } return n; } friend ostream &operator<<(ostream &os, const Node &rhs) { os << "\n"; os << "L : ( " << rhs.lx << ", " << rhs.ly << " )\n"; os << "M : ( " << rhs.x << ", " << rhs.y << " )\n"; os << "R : ( " << rhs.rx << ", " << rhs.ry << " )\n"; os << "seq : {"; for (auto &x : rhs.seq) os << x << ", "; os << "} \n"; return os; } friend bool operator<(const Node &lhs, const Node &rhs) { return lhs.x * rhs.y < rhs.x * lhs.y; } friend bool operator==(const Node &lhs, const Node &rhs) { return lhs.x == rhs.x and lhs.y == rhs.y; } }; /** * @brief Stern-Brocot Tree */ // 極小から始まる下向き凸包の頂点列挙 // (xl, yl) 始点, x in [xl, xr] // inside(x, y) : (x, y) が凸包内部か? // candicate(x, y, c, d) : (x, y) が凸包外部にあるとする。 // 凸包内部の点 (x + sc, y + sd) が存在すればそのような s を返す // (ただし s は誤差でズレてもいいように s ± 2 を探索する仕様) // 存在しなければ任意の値 (-1 でもよい) を返す template vector> enumerate_convex( Int xl, Int yl, Int xr, const function& inside, const function& candicate) { assert(xl <= xr); // inside かつ x <= xr auto f = [&](Int x, Int y) { return x <= xr && inside(x, y); }; // (a, b) から (c, d) 方向に進めるだけ進む auto go = [&](Int a, Int b, Int c, Int d) -> Int { assert(f(a, b)); Int r = 1, s = 0; while (f(a + r * c, b + r * d)) r *= 2; while ((r /= 2) != 0) { if (f(a + r * c, b + r * d)) s += r, a += r * c, b += r * d; } return s; }; // (a, b) が out, (a + c * k, b + d * k) が in とする // out の間進めるだけ進む auto go2 = [&](Int a, Int b, Int c, Int d, Int k) { assert(!f(a, b) and f(a + c * k, b + d * k)); Int ok = 0, ng = k; while (ok + 1 < ng) { Int m = (ok + ng) / 2; (f(a + c * m, b + d * m) ? ng : ok) = m; } return ok; }; vector> ps; Int x = xl, y = yl; assert(f(x, y) and go(x, y, 0, -1) == 0); ps.emplace_back(x, y); SternBrocotTreeNode sb; while (x < xr) { Int a, b; if (f(x + 1, y)) { a = 1, b = 0; } else { while (!f(x + sb.lx, y + sb.ly)) { assert(!sb.seq.empty()); Int bc = sb.seq.back(); sb.go_parent(bc < 0 ? -bc : bc); } while (true) { assert(f(x + sb.lx, y + sb.ly)); assert(!f(x + sb.rx, y + sb.ry)); if (f(x + sb.lx + sb.rx, y + sb.ly + sb.ry)) { Int s = go(x + sb.lx, y + sb.ly, sb.rx, sb.ry); assert(s > 0); sb.go_right(s); } else { Int c = candicate(x + sb.rx, y + sb.ry, sb.lx, sb.ly); if (sb.lx) c = min(c, (xr - x - sb.rx) / sb.lx); Int s = -1; // 念のため周囲を調べる(フェイルセーフ) for (Int d = -2; d <= 2; d++) { Int v = c + d; if (v > 0 && f(x + sb.lx * v + sb.rx, y + sb.ly * v + sb.ry)) { s = v; break; } } if (s == -1) { a = sb.lx, b = sb.ly; break; } else { Int t = go2(x + sb.rx, y + sb.ry, sb.lx, sb.ly, s); sb.go_left(t); } } } } Int s = go(x, y, a, b); x += a * s, y += b * s; ps.emplace_back(x, y); } return ps; } // sum_{0 <= i < N} (ai + b) // m template T sum_of_floor(T n, T m, T a, T b) { T ret = 0; if (a >= m) ret += (n - 1) * n * (a / m) / 2, a %= m; if (b >= m) ret += n * (b / m), b %= m; T y = (a * n + b) / m; if (y == 0) return ret; T x = y * m - b; ret += (n - (x + a - 1) / a) * y; ret += sum_of_floor(y, a, m, (a - x % a) % a); return ret; } // verify www.codechef.com/viewsolution/36222026 // count x : ax + b mod m < yr, 0 <= x < xr template T mod_affine_range_counting(T a, T b, T m, T xr, T yr) { assert(0 <= yr && yr <= m); return sum_of_floor(xr, m, a, b + m) - sum_of_floor(xr, m, a, b + m - yr); } using namespace Nyaan; template struct LazyMontgomeryModInt { using mint = LazyMontgomeryModInt; using i32 = int32_t; using u32 = uint32_t; using u64 = uint64_t; static constexpr u32 get_r() { u32 ret = mod; for (i32 i = 0; i < 4; ++i) ret *= 2 - mod * ret; return ret; } static constexpr u32 r = get_r(); static constexpr u32 n2 = -u64(mod) % mod; static_assert(mod < (1 << 30), "invalid, mod >= 2 ^ 30"); static_assert((mod & 1) == 1, "invalid, mod % 2 == 0"); static_assert(r * mod == 1, "this code has bugs."); u32 a; constexpr LazyMontgomeryModInt() : a(0) {} constexpr LazyMontgomeryModInt(const int64_t &b) : a(reduce(u64(b % mod + mod) * n2)){}; static constexpr u32 reduce(const u64 &b) { return (b + u64(u32(b) * u32(-r)) * mod) >> 32; } constexpr mint &operator+=(const mint &b) { if (i32(a += b.a - 2 * mod) < 0) a += 2 * mod; return *this; } constexpr mint &operator-=(const mint &b) { if (i32(a -= b.a) < 0) a += 2 * mod; return *this; } constexpr mint &operator*=(const mint &b) { a = reduce(u64(a) * b.a); return *this; } constexpr mint &operator/=(const mint &b) { *this *= b.inverse(); return *this; } constexpr mint operator+(const mint &b) const { return mint(*this) += b; } constexpr mint operator-(const mint &b) const { return mint(*this) -= b; } constexpr mint operator*(const mint &b) const { return mint(*this) *= b; } constexpr mint operator/(const mint &b) const { return mint(*this) /= b; } constexpr bool operator==(const mint &b) const { return (a >= mod ? a - mod : a) == (b.a >= mod ? b.a - mod : b.a); } constexpr bool operator!=(const mint &b) const { return (a >= mod ? a - mod : a) != (b.a >= mod ? b.a - mod : b.a); } constexpr mint operator-() const { return mint() - mint(*this); } constexpr mint operator+() const { return mint(*this); } constexpr mint pow(u64 n) const { mint ret(1), mul(*this); while (n > 0) { if (n & 1) ret *= mul; mul *= mul; n >>= 1; } return ret; } constexpr mint inverse() const { int x = get(), y = mod, u = 1, v = 0, t = 0, tmp = 0; while (y > 0) { t = x / y; x -= t * y, u -= t * v; tmp = x, x = y, y = tmp; tmp = u, u = v, v = tmp; } return mint{u}; } friend ostream &operator<<(ostream &os, const mint &b) { return os << b.get(); } friend istream &operator>>(istream &is, mint &b) { int64_t t; is >> t; b = LazyMontgomeryModInt(t); return (is); } constexpr u32 get() const { u32 ret = reduce(a); return ret >= mod ? ret - mod : ret; } static constexpr u32 get_mod() { return mod; } }; using namespace std; // コンストラクタの MAX に 「C(n, r) や fac(n) でクエリを投げる最大の n 」 // を入れると倍速くらいになる // mod を超えて前計算して 0 割りを踏むバグは対策済み template struct Binomial { vector f, g, h; Binomial(int MAX = 0) { assert(T::get_mod() != 0 && "Binomial()"); f.resize(1, T{1}); g.resize(1, T{1}); h.resize(1, T{1}); if (MAX > 0) extend(MAX + 1); } void extend(int m = -1) { int n = f.size(); if (m == -1) m = n * 2; m = min(m, T::get_mod()); if (n >= m) return; f.resize(m); g.resize(m); h.resize(m); for (int i = n; i < m; i++) f[i] = f[i - 1] * T(i); g[m - 1] = f[m - 1].inverse(); h[m - 1] = g[m - 1] * f[m - 2]; for (int i = m - 2; i >= n; i--) { g[i] = g[i + 1] * T(i + 1); h[i] = g[i] * f[i - 1]; } } T fac(int i) { if (i < 0) return T(0); while (i >= (int)f.size()) extend(); return f[i]; } T finv(int i) { if (i < 0) return T(0); while (i >= (int)g.size()) extend(); return g[i]; } T inv(int i) { if (i < 0) return -inv(-i); while (i >= (int)h.size()) extend(); return h[i]; } T C(int n, int r) { if (n < 0 || n < r || r < 0) return T(0); return fac(n) * finv(n - r) * finv(r); } inline T operator()(int n, int r) { return C(n, r); } template T multinomial(const vector& r) { static_assert(is_integral::value == true); int n = 0; for (auto& x : r) { if (x < 0) return T(0); n += x; } T res = fac(n); for (auto& x : r) res *= finv(x); return res; } template T operator()(const vector& r) { return multinomial(r); } T C_naive(int n, int r) { if (n < 0 || n < r || r < 0) return T(0); T ret = T(1); r = min(r, n - r); for (int i = 1; i <= r; ++i) ret *= inv(i) * (n--); return ret; } T P(int n, int r) { if (n < 0 || n < r || r < 0) return T(0); return fac(n) * finv(n - r); } // [x^r] 1 / (1-x)^n T H(long long n, long long r) { if (n < 0 || r < 0) return T(0); return r == 0 ? 1 : C(n + r - 1, r); } }; // using namespace Nyaan; using mint = LazyMontgomeryModInt<998244353>; // using mint = LazyMontgomeryModInt<1000000007>; using vm = vector; using vvm = vector; Binomial C; mint naive(ll N) { mint ans = 0; rep1(H, N) { ll x = (2 * N + 1) / (2 * H + 1); if (x < 3) break; ans += (x - 1) / 2; } return ans; } mint calc(ll N) { // (-2x+1)(2y+1)>=2N+2 auto inside = [&](i128 x, i128 y) -> bool { return (-2 * x + 1) * (2 * y + 1) >= 2 * N + 2; }; auto candicate = [&](i128 x, i128 y, i128 c, i128 d) -> i128 { if (c == 0) return infLL; i128 a = x * d - y * c; // (-2x+1)(2y+1)=2N+2 // xd-yc=a // (-2x+1)(2yc+c)=c(2N+2) // (-2x+1)(2(xd-a)+c)=c(2N+2) // (-2x+1)(2dx+(c-2a))=const i128 A = -2 * 2 * d; i128 B = -2 * (c - 2 * a) + 2 * d; long double x_dst = -1.0l * B / (2 * A); i128 res = max(1, (x_dst - x) / c + 0.5); trc(x, y, c, d, res); return res; }; auto cv = enumerate_convex(-(N + 1), 0, 0, inside, candicate); trc(cv); mint ans = 0; rep(i, sz(cv) - 1) { ll y = cv[i].se; ll dx = cv[i + 1].fi - cv[i].fi; ll dy = cv[i + 1].se - cv[i].se; mint cur = sum_of_floor(dx, dx, dy, dx - 1) - dx; cur += mint{dx} * y; ans += cur; } return ans + 1; } void q() { inl(N); out(calc(N)); } void Nyaan::solve() { int t = 1; in(t); while (t--) q(); }