// bagutteru ue ni osoi... #include "template/template.hpp" // #include "math/stern-brocot-tree.hpp" // #include "modint/montgomery-modint.hpp" #include "modulo/binomial.hpp" // using namespace Nyaan; using mint = LazyMontgomeryModInt<998244353>; // using mint = LazyMontgomeryModInt<1000000007>; using vm = vector; using vvm = vector; 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; }; i128 xl = -(N + 1), yl = 0, xr = 0; /* auto candicate = [&](i128 x, i128 y, i128 c, i128 d) { 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))=c(2N+2) 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; }; */ using Int = i128; vector> cv; { using ld = long double; // inside かつ x <= xr auto f = [&](Int x, Int y) { return x <= xr && inside(x, y); }; // (a, b) から (c, d) 方向に進めるだけ進む auto go_naive = [&](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; }; auto go = [&](Int a, Int b, Int c, Int d) -> Int { assert(f(a, b)); // trc("go1", a, b, c, d); if (c == 0) { assert(d == -1); assert(!f(a + c, b + d)); return 0; } i128 rhs = a * d - b * c; // (-2x+1)(2y+1)=2N+2 // xd-yc=r // (-2x+1)(2yc+c)=c(2N+2) // (-2x+1)(2(xd-r)+c)=c(2N+2) // (-2x+1)(2dx+(c-2r))=c(2N+2) i128 A = -2 * 2 * d; i128 B = -2 * (c - 2 * rhs) + 2 * d; i128 C = c - 2 * rhs - c * (2 * N + 2); ld sqrtD = sqrtl(ld(B) * B - 4.0l * A * C); if (isnan(sqrtD)) sqrtD = 0; if (A == 0) trc(a, b, c, d, A, B, C); ld x1 = A != 0 ? (-B - sqrtD) / (2 * A) : -1.0l * C / B; i128 s = max(0, (min(xr, x1) - a) / c + 0.5l); trc(s); while (!f(a + s * c, b + s * d)) s--; while (f(a + (s + 1) * c, b + (s + 1) * d)) s++; trc("go1", a, b, c, d, s); assert(go_naive(a, b, c, d) == s); return s; }; // (a, b) が out, (a + c * k, b + d * k) が in とする // out の間進めるだけ進む // 無限に進めるときは -1 auto go2 = [&](Int a, Int b, Int c, Int d) -> Int { assert(!f(a, b) and !f(a + c, b + d)); if (c == 0) { assert(d == 1); // (-2x+1)(2y+1)=2N+2 Int q = ((2 * N + 2) / (-2 * a + 1) - 1) / 2; while ((-2 * a + 1) * (2 * q + 1) < 2 * N + 2) q++; trc("go2", a, b, c, d, q); return (q - b) - 1; } i128 rhs = a * d - b * 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))=c(2N+2) i128 A = -2 * 2 * d; i128 B = -2 * (c - 2 * rhs) + 2 * d; i128 C = c - 2 * rhs - c * (2 * N + 2); ld sqrtD = sqrtl(ld(B) * B - 4.0l * A * C); if (isnan(sqrtD)) sqrtD = 0; ld x0 = A == 0 ? (-B + sqrtD) / (2 * A) : -C / B; i128 s = max(0, (min(xr, x0) - a) / c + 0.5l); trc("go2", a, b, c, d, s); if (s - 1 > 0 and !f(a + (s - 1) * c, b + (s - 1) * d)) return s - 2; if (s - 0 > 0 and !f(a + (s - 0) * c, b + (s - 0) * d)) return s - 1; if (s + 1 > 0 and !f(a + (s + 1) * c, b + (s + 1) * d)) return s - 0; if (s + 2 > 0 and !f(a + (s + 2) * c, b + (s + 2) * d)) return s + 1; if (s + 3 > 0 and !f(a + (s + 3) * c, b + (s + 3) * d)) return s + 2; return -1; }; Int x = xl, y = yl; assert(f(x, y) and go(x, y, 0, -1) == 0); cv.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 t = go2(x + sb.rx, y + sb.ry, sb.lx, sb.ly); if (t <= 0) { a = sb.lx, b = sb.ly; break; } else { sb.go_left(t); } } } } Int s = go(x, y, a, b); x += a * s, y += b * s; cv.emplace_back(x, y); } } mint ans = 0; Binomial C; 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; ll g = gcd(dx, dy); mint cur = (mint{dx + 1} * (dy + 1) - (g + 1)) * C.inv(2) - dx - dy; 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(); }