#include <iostream>long long solve(long long a, long long b, long long c, long long n) { long long n2 = n * (n + 1) / 2; if (a >= c || b >= c) return solve(a % c, b % c, c, n) + (a / c) * n2 + (b / c) * (n + 1); long long m = (a * n + b) / c; if (!m) return 0; return m * n - solve(c, c - b - 1, a, m - 1);}int main() { int t; std::cin >> t; for (; t; --t) { int a, b, c, n; std::cin >> n >> c >> a >> b; std::cout << solve(a, b, c, n - 1) << '\n'; } return 0;}
还有另一处细节需要处理。上图中间部分的直线的截距是负数,这意味着还没有回到之前的初始情形。要让截距恢复为非负数,只需要将直线(中间部分的实线)向左平移一个单位。这样做不会漏掉任何格点,因为翻转前的蓝色点阵中没有纵坐标为零的点,翻转后也就不存在横坐标为零的点。最后,直线方程就变为 y=(cx+c−b−1)/a;同时,点阵的横坐标的上界也从 m 变成了 m−1。这一步骤可以归纳为算式
本题的另一个特点是,g 和 h 在递归计算时,会相互交错。因此,需要将 (f,g,h) 作为三元组同时递归。
#include <iostream>struct Data { int f, g, h;};Data solve(long long a, long long b, long long c, long long n) { constexpr long long M = 998244353; constexpr long long i2 = (M + 1) / 2; constexpr long long i6 = (M + 1) / 6; long long n2 = (n + 1) * n % M * i2 % M; long long n3 = (2 * n + 1) * (n + 1) % M * n % M * i6 % M; Data res = {0, 0, 0}; if (a >= c || b >= c) { auto tmp = solve(a % c, b % c, c, n); long long aa = a / c, bb = b / c; res.f = (tmp.f + aa * n2 + bb * (n + 1)) % M; res.g = (tmp.g + aa * n3 + bb * n2) % M; res.h = (tmp.h + 2 * bb * tmp.f % M + 2 * aa * tmp.g % M + aa * aa % M * n3 % M + bb * bb % M * (n + 1) % M + 2 * aa * bb % M * n2 % M) % M; return res; } long long m = (a * n + b) / c; if (!m) return res; auto tmp = solve(c, c - b - 1, a, m - 1); res.f = (m * n - tmp.f + M) % M; res.g = (m * n2 + (M - tmp.f) * i2 + (M - tmp.h) * i2) % M; res.h = (n * m % M * m - tmp.f - tmp.g * 2 + 3 * M) % M; return res;}int main() { int t; std::cin >> t; for (; t; --t) { int n, a, b, c; std::cin >> n >> a >> b >> c; auto res = solve(a, b, c, n); std::cout << res.f << ' ' << res.h << ' ' << res.g << '\n'; } return 0;}
#include <cmath>#include <iostream>long long r;long double sqrt_r;long long gcd(long long a, long long b) { return b ? gcd(b, a % b) : a; }unsigned long long f(long long a, long long b, long long c, long long n) { if (!n) return 0; auto d = gcd(a, gcd(b, c)); a /= d; b /= d; c /= d; unsigned long long k = (a * sqrt_r + b) / c; if (k) { return n * (n + 1) / 2 * k + f(a, b - c * k, c, n); } else { unsigned long long m = n * (a * sqrt_r + b) / c; return n * m - f(c * a, -c * b, a * a * r - b * b, m); }}unsigned long long solve(long long n, long long r) { long long sqr = sqrt_r = sqrtl(r); if (r == sqr * sqr) return r % 2 ? (n % 2 ? -1 : 0) : n; return n - 2 * f(1, 0, 1, n) + 4 * f(1, 0, 2, n);}int main() { int t; std::cin >> t; for (; t; --t) { int n; std::cin >> n >> r; long long res = solve(n, r); std::cout << res << '\n'; } return 0;}
#include <iostream>void solve(int a, int b, int& p, int& q, int c, int d) { if ((a / b + 1) * d < c) { p = a / b + 1; q = 1; } else { solve(d, c - d * (a / b), q, p, b, a % b); p += q * (a / b); }}int main() { int a, b, c, d, p, q; while (std::cin >> a >> b >> c >> d) { solve(a, b, p, q, c, d); std::cout << p << '/' << q << '\n'; } return 0;}
// Class T implements the monoid.// Assume that it provides a multiplication operator// and a default constructor returning the unity in the monoid.// Binary exponentiation.template <typename T>T pow(T a, int b) { T res; for (; b; b >>= 1) { if (b & 1) res = res * a; a = a * a; } return res;}// Universal Euclidean algorithm.template <typename T>T euclid(int a, int b, int c, int n, T U, T R) { if (b >= c) return pow(U, b / c) * euclid(a, b % c, c, n, U, R); if (a >= c) return euclid(a % c, b, c, n, U, pow(U, a / c) * R); auto m = ((long long)a * n + b) / c; if (!m) return pow(R, n); return pow(R, (c - b - 1) / a) * U * euclid(c, (c - b - 1) % a, a, m - 1, R, U) * pow(R, n - (c * m - b - 1) / a);}
#include <array>#include <iostream>// Switch between matrix and info merging approaches.#define MATRIX 1// --8<-- [start:euclidean]// Class T implements the monoid.// Assume that it provides a multiplication operator// and a default constructor returning the unity in the monoid.// Binary exponentiation.template <typename T>T pow(T a, int b) { T res; for (; b; b >>= 1) { if (b & 1) res = res * a; a = a * a; } return res;}// Universal Euclidean algorithm.template <typename T>T euclid(int a, int b, int c, int n, T U, T R) { if (b >= c) return pow(U, b / c) * euclid(a, b % c, c, n, U, R); if (a >= c) return euclid(a % c, b, c, n, U, pow(U, a / c) * R); auto m = ((long long)a * n + b) / c; if (!m) return pow(R, n); return pow(R, (c - b - 1) / a) * U * euclid(c, (c - b - 1) % a, a, m - 1, R, U) * pow(R, n - (c * m - b - 1) / a);}// --8<-- [end:euclidean]#if MATRIXtemplate <size_t N>struct Matrix { std::array<long long, N * N> mat; auto loc(size_t i, size_t j) const { return mat[i * N + j]; } auto& loc(size_t i, size_t j) { return mat[i * N + j]; } Matrix() : mat{} { for (size_t i = 0; i != N; ++i) { loc(i, i) = 1; } } Matrix operator*(const Matrix& rhs) const { Matrix res; res.mat.fill(0); for (size_t i = 0; i != N; ++i) { for (size_t k = 0; k != N; ++k) { for (size_t j = 0; j != N; ++j) { res.loc(i, j) += loc(i, k) * rhs.loc(k, j); } } } return res; }};long long solve(int a, int b, int c, int n) { if (!n) return 0; Matrix<3> U, R; U.loc(0, 1) = R.loc(1, 2) = 1; auto res = euclid(a, b, c, n, U, R); return res.loc(0, 2);}#elsestruct Info { long long x, y, s; Info() : x(0), y(0), s(0) {} Info operator*(const Info& rhs) const { Info res; res.x = x + rhs.x; res.y = y + rhs.y; res.s = s + rhs.s + rhs.x * y; return res; }};long long solve(int a, int b, int c, int n) { if (!n) return 0; Info U, R; U.y = 1; R.x = 1; auto res = euclid(a, b, c, n, U, R); return res.s;}#endifint main() { int t; std::cin >> t; for (; t; --t) { int a, b, c, n; std::cin >> n >> c >> a >> b; std::cout << solve(a, b, c, n - 1) << '\n'; } return 0;}
#include <iostream>template <typename T>T pow(T a, int b) { T res; for (; b; b >>= 1) { if (b & 1) res = res * a; a = a * a; } return res;}template <typename T>T euclid(int a, int b, int c, int n, T U, T R) { if (b >= c) return pow(U, b / c) * euclid(a, b % c, c, n, U, R); if (a >= c) return euclid(a % c, b, c, n, U, pow(U, a / c) * R); auto m = ((long long)a * n + b) / c; if (!m) return pow(R, n); return pow(R, (c - b - 1) / a) * U * euclid(c, (c - b - 1) % a, a, m - 1, R, U) * pow(R, n - (c * m - b - 1) / a);}constexpr int M = 998244353;struct Info { long long x, y, s, t, u; Info() : x(0), y(0), s(0), t(0), u(0) {} Info operator*(const Info& rhs) const { Info res; res.x = (x + rhs.x) % M; res.y = (y + rhs.y) % M; res.s = (s + rhs.s + rhs.x * y) % M; auto tmp = (rhs.x * (rhs.x + 1) / 2 + x * rhs.x) % M; res.t = (t + rhs.t + x * rhs.s + tmp * y) % M; res.u = (u + rhs.u + 2 * y * rhs.s + rhs.x * y % M * y) % M; return res; }};void solve(int a, int b, int c, int n) { Info U, R; U.y = 1; R.x = 1; auto res = euclid(a, b, c, n, U, R); auto f = (res.s + b / c) % M; auto g = res.t; auto h = (res.u + (long long)(b / c) * (b / c)) % M; std::cout << f << ' ' << h << ' ' << g << '\n';}int main() { int t; std::cin >> t; for (; t; --t) { int a, b, c, n; std::cin >> n >> a >> b >> c; solve(a, b, c, n); } return 0;}
#include <algorithm>#include <cmath>#include <iostream>template <typename T>T pow(T a, int b) { T res; for (; b; b >>= 1) { if (b & 1) res = res * a; a = a * a; } return res;}struct LinearTransform { int u, v; LinearTransform() : u(0), v(1) {} LinearTransform operator*(const LinearTransform& rhs) const { LinearTransform res; res.u = u + v * rhs.u; res.v = v * rhs.v; return res; } int eval(int x) const { return u + v * x; }};int solve(int n, int r) { long double sqrt_r = sqrtl(r); int sqr = sqrt_r; if (r == sqr * sqr) return sqr % 2 ? (n % 2 ? -1 : 0) : n; int P = 0, Q = 1, D = r, val = 0; LinearTransform U, R; U.v = -1; R.u = 1; while (n) { int a = (P + sqr) / Q; R = pow(U, a) * R; int m = ((P + sqrt_r) / Q - a) * n; P = a * Q - P; Q = (D - P * P) / Q; int rem = n - (int)(m * (P + sqrt_r) / Q); val = pow(R, rem).eval(val); std::swap(U, R); n = m; } return val;}int main() { int t; std::cin >> t; for (; t; --t) { int n, r; std::cin >> n >> r; std::cout << solve(n, r) << '\n'; } return 0;}
通常考虑的问题中,b 都与 a 同阶,O(log(b/c)) 这一项可以忽略。而且,如果在调用万能欧几里得算法前,首先进行了一轮类欧几里得算法的取模,消除 b 的影响,这一项的快速幂的复杂度是可以规避的。这其实是因为通常的问题中,U 的初始形式较为特殊,它的幂次有着更简单的形式,不需要通过快速幂计算。比如正文的例子中,U⌊b/a⌋ 的结果,就是将 U 中不在对角线上的那个 1 替换成 ⌊b/a⌋,而无需用快速幂计算。 ↩