Euclidean-like Algorithm

Introduction

The Euclidean-like algorithm was proposed by Hong Huadun during the 2016 Winter Camp camper exchange. It is commonly used to solve summation problems for sequences of the form

⌊ai+bc⌋\left\lfloor\dfrac{ai+b}{c}\right\rfloor

for sequences of this form (indexed by ii). Its main idea is to use the recursive structure of fractions to reduce the problem to a smaller one and solve it recursively. Because the recursive structure of fractions has a direct connection with the Euclidean algorithm and a direct link to it in continued-fraction expansion, this summation method is also called the Euclidean-like algorithm.

Methods such as continued fractions and the Stern–Brocot tree also characterize the recursive structure of fractions, so problems solvable with the Euclidean-like algorithm can usually be solved with these methods as well. Compared with them, the Euclidean-like algorithm is generally easier to understand and its implementation is more concise.

Euclidean-like Algorithm

The simplest example is the following summation problem:

f(a,b,c,n)=∑i=0n⌊ai+bc⌋,f(a,b,c,n)=\sum_{i=0}^n\left\lfloor \frac{ai+b}{c} \right\rfloor,

where a,b,c,na,b,c,n are all positive integers.

Algebraic Solution

First, taking a,ba,b modulo cc simplifies the problem and reduces it to the case 0≤a,b<c0\le a,b<c:

f(a,b,c,n)=∑i=0n⌊ai+bc⌋=∑i=0n⌊(⌊ac⌋c+(a mod c))i+(⌊bc⌋c+(b mod c))c⌋=∑i=0n(⌊ac⌋i+⌊bc⌋+⌊(a mod c)i+(b mod c)c⌋)=n(n+1)2⌊ac⌋+(n+1)⌊bc⌋+f(a mod c,b mod c,c,n).\begin{aligned} f(a,b,c,n)&=\sum_{i=0}^n\left\lfloor \frac{ai+b}{c} \right\rfloor\\ &=\sum_{i=0}^n\left\lfloor \frac{\left(\left\lfloor\frac{a}{c}\right\rfloor c+(a\bmod c)\right)i+\left(\left\lfloor\frac{b}{c}\right\rfloor c+(b\bmod c)\right)}{c}\right\rfloor\\ &=\sum_{i=0}^n\left(\left\lfloor\frac{a}{c}\right\rfloor i+\left\lfloor\frac{b}{c}\right\rfloor+\left\lfloor\frac{\left(a\bmod c\right)i+\left(b\bmod c\right)}{c} \right\rfloor\right)\\ &=\frac{n(n+1)}{2}\left\lfloor\frac{a}{c}\right\rfloor +(n+1)\left\lfloor\frac{b}{c}\right\rfloor+f(a\bmod c,b\bmod c,c,n). \end{aligned}

Now consider the transformed problem. Let

m=⌊an+bc⌋.m = \left\lfloor \frac{an+b}{c} \right\rfloor.

Then the original problem can be written as a double sum:

∑i=0n⌊ai+bc⌋=∑i=0n∑j=0m−1[j<⌊ai+bc⌋].\sum_{i=0}^n\left\lfloor \frac{ai+b}{c} \right\rfloor =\sum_{i=0}^n\sum_{j=0}^{m-1}\left[j<\left\lfloor \frac{ai+b}{c} \right\rfloor\right].

To reverse the order of summation, we need to compute, for each jj, the range of ii satisfying the condition. To do so, transform the condition:

j<⌊ai+bc⌋=⌈ai+b+1c⌉−1  ⟺  j+1<⌈ai+b+1c⌉  ⟺  j+1<ai+b+1c  ⟺  cj+c−b−1a<i  ⟺  ⌊cj+c−b−1a⌋<i.\begin{aligned} &j<\left\lfloor \frac{ai+b}{c} \right\rfloor = \left\lceil \frac{ai+b+1}{c} \right\rceil-1\\ &\iff j + 1 < \left\lceil \frac{ai+b+1}{c} \right\rceil \iff j+1< \frac{ai+b+1}{c} \\ &\iff \dfrac{cj+c-b-1}{a} < i \iff \left\lfloor\dfrac{cj+c-b-1}{a}\right\rfloor < i. \end{aligned}

The properties of the floor and ceiling functions were used repeatedly during these transformations. Substituting the transformed condition, the original expression becomes:

f(a,b,c,n)=∑j=0m−1∑i=0n[i>⌊cj+c−b−1a⌋]=∑j=0m−1(n−⌊cj+c−b−1a⌋)=nm−f(c,c−b−1,a,m−1).\begin{aligned} f(a,b,c,n)&=\sum_{j=0}^{m-1} \sum_{i=0}^n\left[i>\left\lfloor\frac{cj+c-b-1}{a}\right\rfloor \right]\\ &=\sum_{j=0}^{m-1}\left(n-\left\lfloor\frac{cj+c-b-1}{a}\right\rfloor\right)\\ &=nm-f\left(c,c-b-1,a,m-1\right). \end{aligned}

Let (a′,b′,c′,n′)=(c,c−b−1,a,m−1)(a',b',c',n')=(c,c-b-1,a,m-1); this returns us to the case a′>c′a'>c' discussed above.

Combining these two transformations, we see that (a,c)(a,c) are repeatedly reduced modulo one another and swapped until a=0a=0. This resembles applying the Euclidean algorithm to (a,c)(a,c), which is why the method is called the Euclidean-like algorithm. Its time complexity is O(log⁡min⁡{a,c})O(\log\min\{a,c\}).

During the computation, the case m=0m=0 may occur, in which case the inner recursion has n=−1n=-1. This does not affect the final result. However, if the algorithm is terminated immediately when m=0m=0, its time complexity can be improved to O(log⁡min⁡{a,c,n})O(\log\min\{a,c,n\}).

Complexity Explanation

The similarity between this algorithm and the Euclidean algorithm makes it easy to show that its time complexity is O(log⁡min⁡{a,c})O(\log\min\{a,c\}). Therefore, it only remains to show that if the algorithm terminates when m=0m=0, its time complexity is also O(log⁡n)O(\log n).

Let m=⌊(an+b)/c⌋m=\lfloor(an+b)/c\rfloor, and write S=mnS=mn and k=m/nk=m/n. These correspond respectively to the area of the lattice-point diagram and the slope of the line in the geometric interpretation (see the next section). For sufficiently large nn, we approximately have k≐a/ck\doteq a/c.

Consider how SS and kk change during the algorithm. In the first modulo operation, nn remains unchanged, and kk approximately changes from a/ca/c to (a mod c)/c(a\bmod c)/c, which corresponds to changing the slope from kk to k−⌊k⌋k-\lfloor k\rfloor; SS also approximately becomes (k−⌊k⌋)(k-\lfloor k\rfloor) times its original value. In the second operation, which swaps the horizontal and vertical coordinates, SS remains approximately unchanged while kk becomes its reciprocal. Thus, if the pair (k,S)(k,S) becomes (k′,S′)(k',S') after the two operations, then k′=(k−⌊k⌋)−1k'=(k-\lfloor k\rfloor)^{-1} and S′=(k−⌊k⌋)SS'=(k-\lfloor k\rfloor)S.

Because 1≤⌊k′⌋≤k′<⌊k′⌋+11\le\lfloor k'\rfloor\le k'<\lfloor k'\rfloor+1, after two rounds of recursive computation, the minimum shrinkage factor of the product is

(k′−⌊k′⌋)(k−⌊k⌋)=1−⌊k′⌋k′<1−⌊k′⌋⌊k′⌋+1=1⌊k′⌋+1≤12.(k'-\lfloor k'\rfloor)(k-\lfloor k\rfloor) = 1-\dfrac{\lfloor k'\rfloor}{k'} < 1-\dfrac{\lfloor k'\rfloor}{\lfloor k'\rfloor+1} = \dfrac{1}{\lfloor k'\rfloor+1}\le \dfrac{1}{2}.

Therefore, the algorithm must terminate after at most O(log⁡S)O(\log S) rounds. Starting from the second round, the SS at the beginning of each round is never greater than the SS after the modulo operation in the preceding round; the latter is approximately kn2kn^2 with k<1k<1, so O(log⁡S)⊆O(log⁡n)O(\log S)\subseteq O(\log n). This proves the claim above.

The reference implementation for the template problem is as follows:

Template Problem Implementation (Library Checker - Sum of Floor of Linear)
#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;
}

Geometric Intuition

This algorithm can also be understood geometrically. The main problems solvable by the Euclidean-like algorithm are lattice-point counting problems below a line.

As shown in the leftmost part of the figure below, the sum is equivalent to counting the lattice points below the line

y=ax+bcy = \dfrac{ax+b}{c}

above the xx-axis (excluding the xx-axis), whose horizontal coordinates lie in [0,n][0,n].

Figure: euclidean 1

First remove the integer parts of the slope and intercept. This is equivalent to separately counting the blue points in the middle part of the figure above. When the slope and intercept are both integers, the blue points form a trapezoidal array: the lattice points in different columns form arithmetic progressions, so their number is easy to compute. After removing these points, the remaining lattice points match the number of red points in the rightmost part of the figure. The problem is reduced to the case where both the slope and intercept are less than one. Since the trapezoid has height n+1n+1 and its two bases have lengths ⌊b/c⌋\lfloor b/c\rfloor and (⌊a/c⌋n+⌊b/c⌋)(\lfloor a/c\rfloor n+\lfloor b/c\rfloor), the trapezoid area formula gives

f(a,b,c,n)=f(a mod c,b mod c,c,n)+12(n+1)(⌊bc⌋+(⌊ac⌋n+⌊bc⌋)).f(a,b,c,n) = f(a\bmod c,b\bmod c,c,n) + \dfrac{1}{2}(n+1)\left(\left\lfloor\dfrac{b}{c}\right\rfloor+\left(\left\lfloor\dfrac{a}{c}\right\rfloor n+\left\lfloor\dfrac{b}{c}\right\rfloor\right)\right).

Next, flip the horizontal and vertical axes. As shown in the leftmost part of the figure below, the red and blue points form a rectangular lattice of horizontal length nn and vertical length m=⌊(an+b)/c⌋m=\lfloor(an+b)/c\rfloor. To count the red points, count the blue points and subtract that number from the number of points in the rectangular lattice. After the flip, the blue lattice in the left half of the figure becomes the red lattice below a line. Moreover, after the flip, the slope is greater than one, returning to the case handled above.

Figure: euclidean 2

The key is how to calculate the equation of the line above the new red lattice. Flipping the coordinate axes in the leftmost part of the figure gives the middle part. The line above the flipped red lattice (the solid line in the middle) does not correspond to the original line (the solid line on the left); instead, it is obtained by shifting the original line slightly to the upper left (the dashed line on the left). This is because directly flipping the line (the solid line on the left) produces the dashed line in the middle, but by definition the lattice points below it include those exactly on the line, causing points on the line to be counted twice. To avoid this, after flipping y=(ax+b)/cy=(ax+b)/c to obtain y=(cx−b)/ay=(cx-b)/a, shift the resulting line slightly downward to obtain y=(cx−b−1)/ay=(cx-b-1)/a; the lattice below it is then exactly the blue lattice before the flip.

There is one more detail to handle. The intercept of the line in the middle part of the figure is negative, so we have not yet returned to the original situation. To make the intercept nonnegative, shift the line (the solid line in the middle) one unit to the left. This does not omit any lattice point because the blue lattice before the flip contains no point with vertical coordinate zero, so after the flip there is no point with horizontal coordinate zero either. The line equation finally becomes y=(cx+c−b−1)/ay=(cx+c-b-1)/a; at the same time, the upper bound of the lattice's horizontal coordinate changes from mm to m−1m-1. This step can be summarized as

f(a,b,c,n)=mn−f(c,c−b−1,a,m−1).f(a,b,c,n) = mn - f(c,c-b-1,a,m-1).

This recursive algorithm works mainly for two reasons:

  • First, the slope of the line repeatedly takes its fractional part and then its reciprocal, which is equivalent to computing the continued-fraction expansion of the line's slope k=a/ck=a/c. Since the continued-fraction expansion of a rational fraction has length O(log⁡min⁡{a,c})O(\log\min\{a,c\}), this process must terminate after O(log⁡min⁡{a,c})O(\log\min\{a,c\}) steps;
  • Second, because the slope of the line is less than one whenever the axes are flipped, intuition suggests that m<nm<n; that is, the range of horizontal coordinates keeps shrinking after each such iteration. The complexity analysis above rigorously shows that after every two iterations, nn is at most half its previous value, so this process must terminate after O(log⁡n)O(\log n) steps.

This is also why the Euclidean-like algorithm has complexity O(log⁡min⁡{a,c,n})O(\log\min\{a,c,n\}) when the slope is rational.

The Euclidean-like algorithm can be extended to the case of an irrational slope using similar geometric intuition; see the examples later in the article for the detailed analysis.

Examples

[Template] Euclidean-like Algorithm

Multiple queries. Given positive integers a,b,c,na,b,c,n, calculate

f(a,b,c,n)=∑i=0n⌊ai+bc⌋,g(a,b,c,n)=∑i=0ni⌊ai+bc⌋,h(a,b,c,n)=∑i=0n⌊ai+bc⌋2.\begin{aligned} f(a,b,c,n) &= \sum_{i=0}^n\left\lfloor \frac{ai+b}{c} \right\rfloor,\\ g(a,b,c,n) &= \sum_{i=0}^ni\left\lfloor \frac{ai+b}{c} \right\rfloor,\\ h(a,b,c,n) &= \sum_{i=0}^n\left\lfloor \frac{ai+b}{c} \right\rfloor^2. \end{aligned}
Solution 1

As in the derivation for ff, recursive expressions for g,hg,h can be obtained.

First, use modulo reduction to reduce the problem to the case 0≤a,b<c0\le a,b<c:

g(a,b,c,n)=g(a mod c,b mod c,c,n)+⌊ac⌋n(n+1)(2n+1)6+⌊bc⌋n(n+1)2,h(a,b,c,n)=h(a mod c,b mod c,c,n)+2⌊bc⌋f(a mod c,b mod c,c,n)+2⌊ac⌋g(a mod c,b mod c,c,n)+⌊ac⌋2n(n+1)(2n+1)6+⌊bc⌋2(n+1)+⌊ac⌋⌊bc⌋n(n+1).\begin{aligned} g(a,b,c,n) &=g(a\bmod c,b\bmod c,c,n)+\left\lfloor\frac{a}{c}\right\rfloor\frac{n(n+1)(2n+1)}{6}+\left\lfloor\frac{b}{c}\right\rfloor\frac{n(n+1)}{2}, \\ h(a,b,c,n)&=h(a\bmod c,b\bmod c,c,n)\\ &\quad+2\left\lfloor\frac{b}{c}\right\rfloor f(a\bmod c,b\bmod c,c,n) +2\left\lfloor\frac{a}{c}\right\rfloor g(a\bmod c,b\bmod c,c,n)\\ &\quad+\left\lfloor\frac{a}{c}\right\rfloor^2\frac{n(n+1)(2n+1)}{6}+\left\lfloor\frac{b}{c}\right\rfloor^2(n+1) +\left\lfloor\frac{a}{c}\right\rfloor\left\lfloor\frac{b}{c}\right\rfloor n(n+1). \end{aligned}

Then, reversing the order of summation gives a further transformation. Similarly, let

m=⌊an+bc⌋.m = \left\lfloor \frac{an+b}{c} \right\rfloor.

For the sum gg, we have

g(a,b,c,n)=∑i=0ni⌊ai+bc⌋=∑i=0n∑j=0m−1i[j<⌊ai+bc⌋]=∑j=0m−1∑i=0ni[i>⌊cj+c−b−1a⌋]=∑j=0m−112(⌊cj+c−b−1a⌋+n+1)(n−⌊cj+c−b−1a⌋)=12mn(n+1)−12∑j=0m−1⌊cj+c−b−1a⌋−12∑j=0m−1⌊cj+c−b−1a⌋2=12mn(n+1)−12f(c,c−b−1,a,m−1)−12h(c,c−b−1,a,m−1).\begin{aligned} g(a,b,c,n)&=\sum_{i=0}^ni\left\lfloor \frac{ai+b}{c} \right\rfloor\\ &=\sum_{i=0}^n \sum_{j=0}^{m-1}i \left[j<\left\lfloor\frac{ai+b}{c}\right\rfloor\right] \\ &=\sum_{j=0}^{m-1}\sum_{i=0}^n i\left[i>\left\lfloor\frac{cj+c-b-1}{a}\right\rfloor \right]\\ &=\sum_{j=0}^{m-1}\dfrac{1}{2}\left(\left\lfloor\frac{cj+c-b-1}{a}\right\rfloor+n+1\right)\left(n-\left\lfloor\frac{cj+c-b-1}{a}\right\rfloor\right)\\ &=\dfrac{1}{2}mn(n+1) - \dfrac{1}{2}\sum_{j=0}^{m-1}\left\lfloor\frac{cj+c-b-1}{a}\right\rfloor - \dfrac{1}{2}\sum_{j=0}^{m-1}\left\lfloor\frac{cj+c-b-1}{a}\right\rfloor^2\\ &=\dfrac{1}{2}mn(n+1) - \dfrac{1}{2}f(c,c-b-1,a,m-1) - \dfrac{1}{2}h(c,c-b-1,a,m-1). \end{aligned}

For the sum hh, we have

h(a,b,c,n)=∑i=0n⌊ai+bc⌋2=∑i=0n∑j=0m−1(2j+1)[j<⌊ai+bc⌋]=∑j=0m−1∑i=0n(2j+1)[i>⌊cj+c−b−1a⌋]=∑j=0m−1(2j+1)(n−⌊cj+c−b−1a⌋)=nm2−∑j=0m−1⌊cj+c−b−1a⌋−2∑j=0m−1j⌊cj+c−b−1a⌋=nm2−f(c,c−b−1,a,m−1)−2g(c,c−b−1,a,m−1).\begin{aligned} h(a,b,c,n)&=\sum_{i=0}^n\left\lfloor \frac{ai+b}{c} \right\rfloor^2\\ &=\sum_{i=0}^n\sum_{j=0}^{m-1}(2j+1)\left[j<\left\lfloor\frac{ai+b}{c}\right\rfloor\right]\\ &=\sum_{j=0}^{m-1}\sum_{i=0}^n(2j+1)\left[i>\left\lfloor\frac{cj+c-b-1}{a}\right\rfloor \right]\\ &=\sum_{j=0}^{m-1}(2j+1)\left(n-\left\lfloor\frac{cj+c-b-1}{a}\right\rfloor\right)\\ &=nm^2 - \sum_{j=0}^{m-1}\left\lfloor\frac{cj+c-b-1}{a}\right\rfloor - 2\sum_{j=0}^{m-1}j\left\lfloor\frac{cj+c-b-1}{a}\right\rfloor\\ &=nm^2 - f(c,c-b-1,a,m-1) - 2g(c,c-b-1,a,m-1). \end{aligned}

From the geometric viewpoint, these nonlinear sums amount to assigning an appropriate weight w(i,j)w(i,j) to every point (i,j)(i,j) in the region. Apart from these weights, the rest of the computation is exactly the same. In general, the weights can be chosen as follows:

∑i=0nir⌊ai+bc⌋s=∑i=0n∑j=0m−1ir((j+1)s−js)[j<⌊ai+bc⌋].\sum_{i=0}^ni^r\left\lfloor \frac{ai+b}{c} \right\rfloor^s = \sum_{i=0}^n\sum_{j=0}^{m-1} i^r\left((j+1)^s-j^s\right)\left[j<\left\lfloor\frac{ai+b}{c}\right\rfloor\right].

Another feature of this problem is that gg and hh interleave during the recursive computation. Therefore, (f,g,h)(f,g,h) must be recursed together as a triple.

#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;
}
[Tsinghua Training 2014] Sum

Multiple queries. Given positive integers nn and rr, calculate

∑d=1n(−1)⌊dr⌋.\sum_{d=1}^n(-1)^{\lfloor d\sqrt{r}\rfloor}.
Solution 1

If rr is a perfect square, then when r\sqrt{r} is even, the sum is nn; otherwise, depending on the parity of nn, the sum alternates between 00 and −1-1. Now consider the case where rr is not a perfect square.

To apply the Euclidean-like algorithm, first transform the sum into a familiar form:

∑d=1n(−1)⌊dr⌋=∑d=1n(1−2(⌊dr⌋ mod 2))=n−2∑d=1n(⌊dr⌋−2⌊⌊dr⌋2⌋)=n−2∑d=1n⌊dr⌋+4∑d=1n⌊dr2⌋=n−2f(n,1,0,1)+4f(n,1,0,2)\begin{aligned} \sum_{d=1}^n(-1)^{\lfloor d\sqrt{r}\rfloor} &= \sum_{d=1}^n\left(1 - 2(\lfloor d\sqrt{r}\rfloor\bmod 2)\right)\\ &= n - 2\sum_{d=1}^n\left(\lfloor d\sqrt{r}\rfloor - 2\left\lfloor\dfrac{\lfloor d\sqrt{r}\rfloor}{2}\right\rfloor\right) \\ &= n - 2\sum_{d=1}^n\lfloor d\sqrt{r}\rfloor + 4 \sum_{d=1}^n\left\lfloor\dfrac{d\sqrt{r}}{2}\right\rfloor\\ &= n - 2f(n,1,0,1) + 4f(n,1,0,2) \end{aligned}

The function ff has the form

f(a,b,c,n)=∑i=1n⌊ar+bci⌋.f(a,b,c,n) = \sum_{i=1}^n\left\lfloor\dfrac{a\sqrt{r}+b}{c}i\right\rfloor.

Unlike the algorithm in the main text, the slope here is no longer rational. Let the slope be

k=ar+bc.k = \dfrac{a\sqrt{r}+b}{c}.

We again discuss two cases. If k≥1k\ge 1, then

f(a,b,c,n)=∑i=1n⌊ki⌋=∑i=1n⌊(k−⌊k⌋)i⌋+⌊k⌋∑i=1ni=⌊k⌋n(n+1)2+f(a,b−c⌊k⌋,c,n).\begin{aligned} f(a,b,c,n) &= \sum_{i=1}^n \lfloor ki\rfloor = \sum_{i=1}^n \lfloor(k-\lfloor k\rfloor)i\rfloor + \lfloor k\rfloor \sum_{i=1}^ni\\ &= \lfloor k\rfloor\dfrac{n(n+1)}{2} + f(a,b-c\lfloor k\rfloor,c,n). \end{aligned}

The problem is reduced to the case where the slope is less than one. If k<1k<1, let m=⌊nk⌋m=\lfloor nk\rfloor. Then

f(a,b,c,n)=∑i=1n⌊ki⌋=∑i=1n∑j=1m[j≤⌊ki⌋]=∑j=1m∑i=1n[i>⌊k−1j⌋]=nm−∑j=1m∑i=1n[i≤⌊k−1j⌋].\begin{aligned} f(a,b,c,n) &= \sum_{i=1}^n \lfloor ki\rfloor = \sum_{i=1}^n\sum_{j=1}^m[j\le\lfloor ki\rfloor]\\ &= \sum_{j=1}^m\sum_{i=1}^n[i>\lfloor k^{-1}j\rfloor] = nm - \sum_{j=1}^m\sum_{i=1}^n[i\le\lfloor k^{-1}j\rfloor]. \end{aligned}

In this derivation, the condition for exchanging ii and jj is simpler than in the main text because the line y=kxy=kx has no lattice points other than the origin. The key is to write the exchanged sum in the form of f(a,b,c,n)f(a,b,c,n), which requires a′,b′,c′a',b',c' to satisfy

k−1=a′r+b′c′.k^{-1} = \dfrac{a'\sqrt{r}+b'}{c'}.

This is not difficult: rationalizing the denominator gives

k−1=car+b=car−cba2r−b2.k^{-1} = \dfrac{c}{a\sqrt{r}+b} = \dfrac{ca\sqrt{r}-cb}{a^2r-b^2}.

Therefore,

a′=ca, b′=−cb, c′=a2r−b2.a'=ca,~b'=-cb,~c'=a^2r-b^2.

This gives

f(a,b,c,n)=nm−f(ca,−cb,a2r−b2,m).f(a,b,c,n) = nm - f(ca,-cb,a^2r-b^2,m).

To avoid integer overflow, divide a,b,ca,b,c by their greatest common divisor at every step. Because this computation is exactly the same as computing the continued fraction of kk, the theory of continued fractions shows that, as long as gcd⁡(a,b,c)=1\gcd(a,b,c)=1, they necessarily remain within the integer range during the computation. In addition, although (a,b,c,n)(a,b,c,n) does not overflow, under this problem's data limits f(a,b,c,n)f(a,b,c,n) may exceed the range of 6464-bit integers. Natural overflow is sufficient and no extra handling is needed; the final result is always in [−n,n][-n,n].

Although the slope never becomes zero, the algorithm still has complexity O(log⁡n)O(\log n), as can be seen from the preceding complexity argument.

#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;
}
Fraction

Given positive integers a,b,c,da,b,c,d, find all values satisfying $a/b [!note]- Solution

This problem is also a classic application of the Stern–Brocot tree; related solutions can be found here. Since it relies only on the recursive structure of fractions, it can likewise be solved using a Euclidean-like algorithm, and can therefore be viewed as an application of the Euclidean-like algorithm.

If there is at least one natural number strictly between a/ba/b and c/dc/d, we can directly take (q,p)=(1,⌊a/b⌋+1)(q,p)=(1,\lfloor a/b\rfloor+1). Otherwise, necessarily

⌊ab⌋≤ab<pq<cd≤⌊ab⌋+1.\left\lfloor\dfrac{a}{b}\right\rfloor \le \dfrac{a}{b} <\dfrac{p}{q} <\dfrac{c}{d}\le\left\lfloor\dfrac{a}{b}\right\rfloor+1.

From this inequality, the integer part of p/qp/q is ⌊a/b⌋\lfloor a/b\rfloor. Remove that integer part directly and then take the reciprocal of the whole expression to determine its fractional part. This is exactly the basic method for determining the continued fraction of p/qp/q. If the final answer is p/qp/q, the algorithm has time complexity O(log⁡min⁡{p,q})O(\log\min\{p,q\}).

There is one subtle issue here: is the lexicographically smallest fraction obtained after taking the reciprocal also the lexicographically smallest fraction before taking the reciprocal? In other words, among fractions satisfying $a/b

ab<rs<rq<pq<cd.\dfrac{a}{b} < \dfrac{r}{s} < \dfrac{r}{q} < \dfrac{p}{q} < \dfrac{c}{d}.

Therefore, r/qr/q is strictly smaller than the current solution under either lexicographic order. This contradicts the assumption, so the algorithm above is correct.

#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;
}

Universal Euclidean Algorithm

The derivation of the Euclidean-like algorithm discussed in the previous section is usually rather cumbersome, and the sums it can solve are mainly those reducible to (weighted) lattice-point counting below a line. This section presents a more general method that abstracts the process above and can solve more problems. It is therefore also called the universal Euclidean algorithm. It likewise uses the recursive structure of fractions, but its approach to reducing the problem differs slightly from that of the Euclidean-like algorithm.

We still consider the most classical summation problem:

f(a,b,c,n)=∑i=1n⌊ai+bc⌋,f(a,b,c,n)=\sum_{i=1}^n\left\lfloor \frac{ai+b}{c} \right\rfloor,

where a,b,c,na,b,c,n are all positive integers.

Problem Transformation

Let the line with parameters (a,b,c,n)(a,b,c,n) be

y=ax+bc, 0<x≤n.y = \frac{ax+b}{c},~0< x\le n.

For this line segment, define a string SS consisting of UU and RR, also called the operation sequence, as follows:

  • The string consists of exactly nn RR's and m=⌊(an+b)/c⌋m=\lfloor(an+b)/c\rfloor UU's;
  • The number of UU's before the ii-th RR is exactly ⌊(ai+b)/c⌋\lfloor(ai+b)/c\rfloor, where i=1,⋯ ,ni=1,\cdots,n.

Geometrically, this is roughly equivalent to starting at the origin: write an RR each time a vertical grid line is crossed to the right, and write a UU each time a horizontal grid line is crossed upward. As shown below:

Figure: euclidean universal

Naturally, this definition must account for several special cases:

  • When an integer lattice point is passed (that is, an upward crossing and a rightward crossing occur simultaneously), write UU before RR;
  • At the start of the string, in addition to the number of grid lines crossed upward in the interval (0,1](0,1], add ⌊b/c⌋\lfloor b/c\rfloor extra UU's;
  • At the end of the string, there must be no extra UU's.

If any part of the geometric description is unclear, refer to the algebraic definition above for help. The geometric description helps explain the algorithm below.

The basic idea of the universal Euclidean algorithm is to regard the UU and RR in the operation sequence as elements of a certain monoid, regard the entire operation sequence as the product of elements in that monoid, and relate the final answer to this product.

For example, in this problem, define a state vector v=(1,y,∑y)v = (1,y,\sum y) to represent the current state after crossing some horizontal and vertical grid lines from the origin. The first component is a constant, the second is the vertical coordinate yy, and the third is the sum being sought. Initially, v=(1,0,0)v=(1,0,0). Each upward crossing increases the vertical coordinate by one, which is equivalent to right-multiplying the state vector by the matrix

U=(110010001).U = \begin{pmatrix}1 & 1 & 0 \\ 0 & 1 & 0 \\ 0 & 0 & 1\end{pmatrix}.

Each rightward crossing adds the current vertical coordinate to the sum, which is equivalent to right-multiplying the state vector by the matrix

R=(100011001).R = \begin{pmatrix}1 & 0 & 0 \\ 0 & 1 & 1 \\ 0 & 0 & 1\end{pmatrix}.

Therefore, the final state is the product (1,0,0)S(1,0,0)S, where SS is understood as the product of the matrices above. The answer is the third component of the final state.

Besides defining elements of the monoid as matrices, they can also be defined as the contribution of an operation sequence to the final result, with the product of operations defined as the combination of the contributions of two operation sequences.

For this problem, define the contribution of each operation sequence as (x,y,∑y)(x,y,\sum y). To explain this notation rigorously, regard each component as a function of the operation sequence: for an operation sequence SS, its contribution is (x(S),y(S),(∑y)(S))(x(S),y(S),(\sum y)(S)). Here, x(S)x(S) and y(S)y(S) are respectively the numbers of RR and UU in SS, that is, the numbers of rightward and upward grid-line crossings. In general, the summation symbol in the last term is defined as follows: for a function f(S)f(S) on operation sequences, define (∑f)(S)(\sum f)(S), or write it as ∑Sf\sum_S f, by the expression below:

∑Sf:=∑{f(S[1,r]):Sr=R}.\sum_S f := \sum\{f(S_{[1,r]}):S_r=R\}.

Here, SrS_r is the rr-th character of SS, and S[1,r]S_{[1,r]} is the prefix consisting of the first rr characters of SS. In other words, this summation notation can be viewed as summing over all prefixes of SS that end in RR. For example,

∑S1=x, ∑Sx=12x(x+1).\sum_S 1 = x,~ \sum_S x = \dfrac{1}{2}x(x+1).

Also, ∑y\sum y is the accumulated number of previous upward grid-line crossings each time a rightward grid line is crossed. For the entire operation sequence, the values of yy at all prefixes ending in RR are the values of ⌊(ai+b)/c⌋\lfloor(ai+b)/c\rfloor for i=1,⋯ ,ni=1,\cdots,n. Therefore, ∑y\sum y computed for the entire operation sequence is exactly the quantity required by the problem.

Initially, U=(0,1,0)U=(0,1,0) and R=(1,0,0)R=(1,0,0). We can further define the product of two elements (x1,y1,s1)(x_1,y_1,s_1) and (x2,y2,s2)(x_2,y_2,s_2) as

(x1,y1,s1)⋅(x2,y2,s2)=(x1+x2,y1+y2,s1+s2+x2y1).(x_1,y_1,s_1)\cdot (x_2,y_2,s_2) = (x_1+x_2,y_1+y_2,s_1+s_2+x_2y_1).

The result of combining the contribution in the last term can be obtained as follows:

∑S1+S2y=∑S1y+∑S2(y+y1)=∑S1y+∑S2y+y1∑S21=s1+s2+x2y1.\sum_{S_1+S_2}y = \sum_{S_1}y + \sum_{S_2}(y+y_1) = \sum_{S_1}y + \sum_{S_2}y + y_1\sum_{S_2}1 = s_1+s_2+x_2y_1.

It is easy to verify that this multiplication is associative and that the identity is (0,0,0)(0,0,0), so these elements form a monoid under this multiplication. The answer is the third component of the product.

Both methods produce the correct result. However, because the matrix method retains more redundant information and therefore has larger constant factors, the second method is more practical for actual problems.

Algorithm

Unlike the global reduction of the Euclidean-like algorithm, the universal Euclidean algorithm reduces the problem by combining these operations in batches. Denote the product of the operations corresponding to the string by

F(a,b,c,n,U,R).F(a,b,c,n,U,R).

The reduction proceeds as follows:

  • When b≥cb\ge c, the operation sequence begins with ⌊b/c⌋\lfloor b/c\rfloor UU's. Compute their product directly and remove these UU's from the sequence. The number of UU's before the ii-th RR is then

    ⌊ai+bc⌋−⌊bc⌋=⌊ai+(b mod c)c⌋.\left\lfloor\dfrac{ai+b}{c}\right\rfloor - \left\lfloor\dfrac{b}{c}\right\rfloor = \left\lfloor\dfrac{ai+(b\bmod c)}{c}\right\rfloor.

    Therefore, this is equivalent to changing the line parameters from (a,b,c,n)(a,b,c,n) to (a,b mod c,c,n)(a,b\bmod c,c,n). Thus, in this case,

    F(a,b,c,n,U,R)=U⌊b/c⌋F(a,b mod c,c,n,U,R).F(a,b,c,n,U,R) = U^{\lfloor b/c\rfloor}F(a,b\bmod c,c,n,U,R).
  • When a≥ca\ge c, there are at least ⌊a/c⌋\lfloor a/c\rfloor UU's before every RR in the operation sequence, so they can be merged into RR. In other words, replace RR by U⌊a/c⌋RU^{\lfloor a/c\rfloor}R. The number of UU's before the ii-th RR in the merged string is

    ⌊ai+bc⌋−⌊ac⌋i=⌊(a mod c)i+bc⌋.\left\lfloor\dfrac{ai+b}{c}\right\rfloor - \left\lfloor\dfrac{a}{c}\right\rfloor i = \left\lfloor\dfrac{(a\bmod c)i+b}{c}\right\rfloor.

    Therefore, this is equivalent to changing the line parameters from (a,b,c,n)(a,b,c,n) to (a mod c,b,c,n)(a\bmod c,b,c,n). Thus, in this case,

    F(a,b,c,n,U,R)=F(a mod c,b,c,n,U,U⌊a/c⌋R).F(a,b,c,n,U,R) = F(a\bmod c,b,c,n,U,U^{\lfloor a/c\rfloor}R).
  • For the remaining case, flip the horizontal and vertical coordinates. This essentially swaps UU and RR, but the parameters of the flipped line must be calculated carefully. From the definition of the operation sequence, determine coefficients (a′,b′,c′,n′)(a',b',c',n') such that the number of RR's before the jj-th UU in the original operation sequence is ⌊(a′j+b′)/c′⌋\lfloor(a'j+b')/c'\rfloor, and there are n′n' UU's in total. By definition,

    n′=⌊an+bc⌋=m,n'=\left\lfloor\dfrac{an+b}{c}\right\rfloor = m,

    The number of RR's before the jj-th UU is the largest ii such that

    ⌊ai+bc⌋<j  ⟺  ai+bc<j  ⟺  i<cj−ba  ⟺  i<⌈cj−ba⌉=⌊cj−b−1a⌋+1.\begin{aligned} \left\lfloor\dfrac{ai+b}{c}\right\rfloor < j &\iff \dfrac{ai+b}{c} < j \iff i < \dfrac{cj-b}{a} \\ &\iff i < \left\lceil\dfrac{cj-b}{a}\right\rceil = \left\lfloor\dfrac{cj-b - 1}{a}\right\rfloor + 1. \end{aligned}

    Therefore, i=⌊(cj−b−1)/a⌋i = \lfloor(cj-b-1)/a\rfloor. This derivation is similar to that of the Euclidean-like algorithm above and likewise uses properties of floor and ceiling functions.

    There are two details to handle:

    • The intercept term −(b+1)/a-(b+1)/a is negative. If the line is shifted one unit to the left, the intercept becomes nonnegative because (c−b−1)/a≥0(c-b-1)/a\ge 0. Therefore, extract the initial segment R⌊(c−b−1)/a⌋UR^{\lfloor(c-b-1)/a\rfloor}U before the swap, and swap only the remaining UU's and RR's;
    • After swapping UU and RR, there are extra UU's at the end. Therefore, before swapping UU and RR, first extract the final segment of RR's, and swap only the remaining UU's and RR's. The number of RR's in this segment is n−⌊(cm−b−1)/a⌋n-\lfloor(cm-b-1)/a\rfloor.

    After removing some characters from the beginning and end, the number of RR's before the jj-th UU becomes:

    ⌊c(j+1)−b−1a⌋−⌊c−b−1a⌋=⌊cj+(c−b−1) mod aa⌋.\left\lfloor\dfrac{c(j+1)-b-1}{a}\right\rfloor - \left\lfloor\dfrac{c-b-1}{a}\right\rfloor = \left\lfloor\dfrac{cj+(c-b-1)\bmod a}{a}\right\rfloor.

    Recall that the number of UU's before the swap is m=⌊(an+b)/c⌋m = \lfloor(an+b)/c\rfloor. The one-unit left shift above requires at least one UU before the swap, that is, m>0m>0. Under this condition, there are two cases:

    • When m>0m>0, after handling the two points above, the operation sequence obtained by swapping UU and RR is the valid sequence corresponding to parameters (c,(c−b−1) mod a,a,m−1)(c,(c-b-1)\bmod a,a,m-1). Thus,

      F(a,b,c,n,U,R)=R⌊(c−b−1)/a⌋UF(c,(c−b−1) mod a,a,m−1,R,U)Rn−⌊(cm−b−1)/a⌋.F(a,b,c,n,U,R) = R^{\lfloor(c-b-1)/a\rfloor}UF(c,(c-b-1)\bmod a,a,m-1,R,U)R^{n-\lfloor(cm-b-1)/a\rfloor}.
    • In the special case m=0m=0, the operation sequence before the swap contains only nn RR's, so no swap is needed and we can return directly:

      F(a,b,c,n,U,R)=Rn.F(a,b,c,n,U,R) = R^n.

      Unlike the Euclidean-like algorithm, this special case of the universal Euclidean algorithm must be handled separately; otherwise negative exponents would make the computation incorrect.

These observations allow the problem to be solved recursively.

Assume that multiplying two monoid elements takes O(1)O(1) time. If powers of these elements are computed by binary exponentiation, the resulting algorithm has complexity O(log⁡max⁡{a,c}+log⁡(b/c))O(\log\max\{a,c\}+\log(b/c)).1

Complexity Explanation

Compared with the (Euclidean-like) algorithm, the universal Euclidean algorithm only adds binary exponentiation. The remaining computation has complexity similar to that of the Euclidean-like algorithm, already shown to be O(log⁡min⁡{a,c,n})O(\log\min\{a,c,n\}). We now need to calculate the total complexity of these binary exponentiations.

Apart from the first iteration, b<cb<c, so each of these iterations involves three binary exponentiations. The total complexity is:

O(log⁡⌊ac⌋+log⁡⌊c−b1−1a1⌋+log⁡(n−⌊cm−b1−1a1⌋)),O\left(\log\left\lfloor\dfrac{a}{c}\right\rfloor+\log\left\lfloor\dfrac{c-b_1-1}{a_1}\right\rfloor+\log\left(n-\left\lfloor\dfrac{cm-b_1-1}{a_1}\right\rfloor\right)\right),

Here, a1=a mod ca_1=a\bmod c, b1=b mod cb_1=b\bmod c, and m=⌊(a1n+b1)/c⌋m=\lfloor(a_1n+b_1)/c\rfloor. The latter two terms satisfy, respectively:

c−b1−1a1≤ca1,n−⌊cm−b1−1a1⌋≤n−cm−b1−1a1+1≤n−c((a1n+b1)/c−1)−b1−1a1+1=c+1a1+1.\begin{aligned} \dfrac{c-b_1-1}{a_1} &\le \dfrac{c}{a_1},\\ n-\left\lfloor\dfrac{cm-b_1-1}{a_1}\right\rfloor &\le n - \dfrac{cm-b_1-1}{a_1} + 1 \\ &\le n - \dfrac{c((a_1n+b_1)/c-1)-b_1-1}{a_1} +1 \\ &= \dfrac{c+1}{a_1}+1. \end{aligned}

Therefore, the complexity of both terms is O(log⁡(c/a1))O(\log(c/a_1)).

In each iteration, the line parameters change from (a,⋅,c,⋅)(a,\cdot,c,\cdot) to (c,⋅,a mod c,⋅)(c,\cdot,a\bmod c,\cdot), and the total complexity of that iteration is

O(log⁡ac+log⁡ca mod c).O\left(\log\dfrac{a}{c}+\log\dfrac{c}{a\bmod c}\right).

Across all recursive rounds, these terms telescope, so the final total complexity is O(log⁡a+log⁡c)=O(log⁡max⁡{a,c})O(\log a+\log c)=O(\log\max\{a,c\}).

Finally, adding the complexity O(log⁡(b/c))O(\log(b/c)) of the binary exponentiation U⌊b/c⌋U^{\lfloor b/c\rfloor} in the first iteration gives total complexity O(log⁡max⁡{a,c}+log⁡(b/c))O(\log\max\{a,c\}+\log(b/c)).

The universal Euclidean algorithm can be written as a unified template; when applying it to a specific problem, only the implementation of the template type T needs to be changed.

Reference Implementation
// 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);
}

The implementation of the template problem obtained using the universal Euclidean algorithm is as follows:

Template Problem Implementation (Library Checker - Sum of Floor of Linear)
#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 MATRIX

template <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);
}

#else

struct 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;
}

#endif

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;
}

Examples

Template: Euclidean-like Algorithm

For multiple queries, given positive integers a,b,c,na,b,c,n, compute

f(a,b,c,n)=∑i=0n⌊ai+bc⌋,g(a,b,c,n)=∑i=0ni⌊ai+bc⌋,h(a,b,c,n)=∑i=0n⌊ai+bc⌋2.\begin{aligned} f(a,b,c,n) &= \sum_{i=0}^n\left\lfloor \frac{ai+b}{c} \right\rfloor,\\ g(a,b,c,n) &= \sum_{i=0}^ni\left\lfloor \frac{ai+b}{c} \right\rfloor,\\ h(a,b,c,n) &= \sum_{i=0}^n\left\lfloor \frac{ai+b}{c} \right\rfloor^2. \end{aligned}
Solution 2

To apply the universal Euclidean algorithm template, first separate the term for i=0i=0. The remaining terms can be viewed as computing ∑y,∑xy,∑y2\sum y,\sum xy,\sum y^2 for the segment with parameters (a,b,c,n)(a,b,c,n). As described in the main text, there are two ways to encode an operation sequence as a monoid element.

Matrix multiplication: define the state vector as (1,x,y,xy,y2,∑y,∑xy,∑y2)(1,x,y,xy,y^2,\sum y,\sum xy,\sum y^2). The initial state is (1,0,0,0,0,0,0,0)(1,0,0,0,0,0,0,0), and the two operations are

U=(1010100001010000001020000001000000001000000001000000001000000001), R=(1100000001000000001101100001001000001001000001000000001000000001).U = \begin{pmatrix} 1 & 0 & 1 & 0 & 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 1 & 0 & 0 & 0 & 0 \\ 0 & 0 & 1 & 0 & 2 & 0 & 0 & 0 \\ 0 & 0 & 0 & 1 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 1 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 1 \end{pmatrix},~ R = \begin{pmatrix} 1 & 1 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 1 & 1 & 0 & 1 & 1 & 0 \\ 0 & 0 & 0 & 1 & 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 0 & 1 & 0 & 0 & 1 \\ 0 & 0 & 0 & 0 & 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 1 \end{pmatrix}.

The final answer is given by the last three components of the vector obtained by multiplying the initial state by the product of these operation matrices.

This method has very large constants and does not pass the problem; the details are included only to aid understanding.

Combining contributions: define the contribution of an operation sequence as (x,y,∑y,∑xy,∑y2)(x,y,\sum y,\sum xy,\sum y^2). The two operations are

U=(0,1,0,0,0), R=(1,0,0,0,0).U = (0,1,0,0,0),~ R = (1,0,0,0,0).

The combined contribution satisfies

∑S1+S2y=∑S1y+∑S2(y+y1)=∑S1y+∑S2y+x2y1,∑S1+S2xy=∑S1xy+∑S2(x+x1)(y+y1)=∑S1xy+∑S2xy+x1∑S2y+y1∑S2x+x1y1∑S21=∑S1xy+∑S2xy+x1∑S2y+12x2(x2+1)y1+x1x2y1,∑S1+S2y2=∑S1y2+∑S2(y+y1)2=∑S1y2+∑S2y2+2y1∑S2y+y12∑S21=∑S1y2+∑S2y2+2y1∑S2y+x2y12.\begin{aligned} \sum_{S_1+S_2} y &= \sum_{S_1}y + \sum_{S_2}(y+y_1) = \sum_{S_1}y + \sum_{S_2}y + x_2y_1,\\ \sum_{S_1+S_2} xy &= \sum_{S_1}xy + \sum_{S_2}(x+x_1)(y+y_1) \\ &= \sum_{S_1}xy + \sum_{S_2}xy + x_1\sum_{S_2}y + y_1\sum_{S_2}x + x_1y_1\sum_{S_2}1\\ &= \sum_{S_1}xy + \sum_{S_2}xy + x_1\sum_{S_2}y + \dfrac{1}{2}x_2(x_2+1)y_1 + x_1x_2y_1,\\ \sum_{S_1+S_2}y^2 &= \sum_{S_1}y^2 + \sum_{S_2}(y+y_1)^2 \\ &= \sum_{S_1}y^2 + \sum_{S_2}y^2 + 2y_1\sum_{S_2}y + y_1^2\sum_{S_2}1 \\ &= \sum_{S_1}y^2 + \sum_{S_2}y^2 + 2y_1\sum_{S_2}y + x_2y_1^2. \end{aligned}

Therefore the multiplication of operations should be defined as

(x1,y1,s1,t1,u1)⋅(x2,y2,s2,t2,u2)=(x1+x2,y1+y2,s1+s2+x2y1,t1+t2+x1s2+(1/2)x2(x2+1)y1+x1x2y1,u1+u2+2y1s2+x2y12).\begin{aligned} &(x_1,y_1,s_1,t_1,u_1)\cdot(x_2,y_2,s_2,t_2,u_2)\\ &= (x_1+x_2,y_1+y_2,s_1+s_2+x_2y_1,\\ &\qquad t_1+t_2+x_1s_2+(1/2)x_2(x_2+1)y_1+x_1x_2y_1,\\ &\qquad u_1+u_2+2y_1s_2+x_2y_1^2). \end{aligned}

Although verifying this definition directly is tedious, these contribution vectors do form a monoid under this multiplication, with identity (0,0,0,0,0)(0,0,0,0,0).

In the general case,

∑S1+S2xrys=∑S1xrys+∑S2(x+x1)r(y+y1)s=∑S1xrys+∑i=0r∑j=0s(ri)(sj)x1r−iy1s−j∑S2xiyj.\begin{aligned} \sum_{S_1+S_2}x^ry^s &= \sum_{S_1}x^ry^s + \sum_{S_2}(x+x_1)^r(y+y_1)^s \\ &= \sum_{S_1}x^ry^s + \sum_{i=0}^r\sum_{j=0}^s\binom{r}{i}\binom{s}{j}x_1^{r-i}y_1^{s-j}\sum_{S_2}x^iy^j. \end{aligned}

Thus, by maintaining contributions of all lower powers, sums of the general form can be computed.

#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;
}
\[Tsinghua Training 2014\] Sum

For multiple queries, given positive integers nn and rr, compute

∑d=1n(−1)⌊dr⌋.\sum_{d=1}^n(-1)^{\lfloor d\sqrt{r}\rfloor}.
Solution 2

First handle the case in which rr is a perfect square; it is identical to the treatment above. Below, assume that rr is not a perfect square.

There are many ways to apply the universal Euclidean algorithm. For example, define a linear transformation for each operation:

U(x)=−x, R(x)=x+1.U(x) = -x,~ R(x) = x + 1.

Define multiplication of operations as composition of linear transformations. The final answer is the value at x=0x=0 of the function obtained by composing the transformations for the operation sequence.

Alternatively, define the contribution of each operation sequence as ((−1)y,∑(−1)y)((-1)^y,\sum(-1)^y). The two operations are

U=(0,−1), R=(1,1).U = (0,-1),~ R = (1,1).

Define the combined contribution by

(u1,v1)⋅(u2,v2)=(u1u2,v1+u1v2).(u_1,v_1)\cdot(u_2,v_2) = (u_1u_2,v_1+u_1v_2).

It is easy to verify that all operations form a monoid under this multiplication, with identity (0,1)(0,1). The final answer is the second component of the product of all elements.

The two approaches agree: if a linear transformation is written as f(x)=u+vxf(x)=u+vx, composition changes its coefficients exactly according to the multiplication above, so the two monoids are isomorphic.

In this problem, the segment parameters are (k,n)(k,n), where k∈Rk\in\mathbf R is the slope. Let the product corresponding to the operation sequence be F(k,n,U,R)F(k,n,U,R). Then:

  • If k≥1k\ge 1, every RR in the operation sequence is preceded by at least ⌊k⌋\lfloor k\rfloor copies of UU, so

    F(k,n,U,R)=F(k−⌊k⌋,n,U,U⌊k⌋R).F(k,n,U,R) = F(k-\lfloor k\rfloor,n,U,U^{\lfloor k\rfloor} R).
  • If k<1k<1, swap UU and RR in the operation sequence and remove the final UU (that is, the original final RR), so

    F(k,n,U,R)=F(k−1,m,R,U)Rn−⌊k−1m⌋.F(k,n,U,R) = F(k^{-1},m,R,U)R^{n-\lfloor k^{-1}m\rfloor}.

The iteration of kk in this algorithm is actually computing the continued-fraction expansion of r\sqrt{r}. The PQa algorithm can be used; the continued-fraction computation and the universal Euclidean iteration can be performed together.

As with the Euclidean-like algorithm, the complexity remains O(log⁡n)O(\log n).

#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;
}

Exercises

Template problems:

Application problems:

References and notes

Footnotes

  1. In the usual problems, bb and aa are of the same order of magnitude, so the term O(log⁡(b/c))O(\log(b/c)) can be ignored. Moreover, if one first performs a modulo-reduction step in the Euclidean-like algorithm before applying the universal algorithm, eliminating the effect of bb, the fast-exponentiation cost of this term can be avoided. This is because the initial form of UU is usually special, so its powers have a simpler form and do not need to be computed by fast exponentiation. For example, in the main text, the result of U⌊b/a⌋U^{\lfloor b/a\rfloor} is obtained by replacing the off-diagonal 11 in UU with ⌊b/a⌋\lfloor b/a\rfloor, without using fast exponentiation. ↩

This content comes from OI Wiki: Euclidean-like Algorithm. See the original for authors and revision history. AIOJ has adapted the formatting, in-site links, and resource paths.

Content is shared under CC BY-SA 4.0 and SATA license; additional terms are governed by the original notice. Thanks to the OI Wiki authors and contributors; per the SATA terms, please visit the OI Wiki repository and star it to show your support.