类欧几里得算法


类欧几里得算法

问题引入

\[f(a, b, c, n) = \sum_{i=0}^n \left\lfloor\frac{ai + b}{c}\right\rfloor \]

其中 \(a, b, c, n\) 是常数,需要 \(\mathcal O(\log n)\) 的做法。

\(a \geq c\)\(b \geq c\),我们可以将 \(a, b\)\(c\) 取模以简化问题。

考虑到

\[x = \left \lfloor \frac{x}{c}\right \rfloor c + x \bmod c \]

\[\begin{split} 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 \lfloor \frac{a}{c}\right \rfloor c + a \bmod c)i + (\left \lfloor \frac{b}{c}\right \rfloor c + b \bmod c)}{c}\right\rfloor \\ &= \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{split} \]

此时一定有 \(a < c\)\(b < c\)

\[S(i)={\left\lfloor\frac{ai + b}{c}\right\rfloor} - 1 \]

再进行转化

\[\begin{split} \sum_{i=0}^n \left\lfloor\frac{ai + b}{c}\right\rfloor &= \sum_{i=0}^n \sum_{j=0}^{S(i)} 1 \\ &= \sum_{j=0}^{S(n)} \sum_{i=0}^n \left[ j \leq S(i) \right] \end{split} \]

考虑到

\[\begin{split} j \leq S(i) &\iff j + 1 \leq {\left\lfloor\frac{ai + b}{c}\right\rfloor} \\ j + 1 \leq {\left\lfloor\frac{ai + b}{c}\right\rfloor} &\iff j + 1 \leq \frac{ai + b}{c} \\ j + 1 \leq \frac{ai + b}{c} &\iff jc + c \leq ai + b \\ jc + c \leq ai + b &\iff jc + c - b \leq ai \\ jc + c - b \leq ai &\iff jc + c - b - 1 < ai \\ jc + c - b - 1 < ai &\iff \left \lfloor \frac{jc + c - b - 1}{a} \right \rfloor < i \end{split} \]

\[\begin {split} \sum_{j=0}^{S(n)} \sum_{i=0}^n \left[ j \leq S(i) \right] &= \sum_{j=0}^{S(n)} \sum_{i=0}^n \left[ i > \left \lfloor \frac{jc + c - b - 1}{a} \right \rfloor\right] \\ &= \sum_{j=0}^{S(n)} \left(n - \left \lfloor \frac{jc + c - b - 1}{a} \right \rfloor \right) \\ &= (S(n) + 1)n - \sum_{j=0}^{S(n)} \left \lfloor \frac{c j + (c - b - 1)}{a} \right \rfloor \\ &= (S(n) + 1)n - f(c, c - b - 1, a, S(n)) \end {split} \]

\[f(a, b, c, n) = (S(n) + 1)n - f(c, c - b - 1, a, S(n)) \]

可以发现,上述式子是一个递归式,我们不断重复上述过程,先取模,后递归,其实就是辗转相除的过程,时间复杂度 \(\mathcal O(\log n)\)

拓展

我们再来推导两个变种求和式

\[g(a, b, c, n) = \sum_{i=0}^n i\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 \]

推导 \(g\)

引理 1.

\[\sum_{i=0}^n i^2 = \frac{n (n+1)(2n+1)}{6} \]

证明如下:

考虑到

\[(n+1)^3=n^3+3n^2+3n+1 \]

\[\begin {split} (n+1)^3-n^3 &= 3n^2+3n+1 \\ n^3-(n-1)^3 &=3(n-1)^2+3(n-1)+1 \\ &\cdots \\ 2^3 - 1^3&=3 \times (2-1)^2+3 \times(2-1) + 1 \\ \end {split} \]

将这 \(n\) 个等式左右两边相加,得到

\[(n+1)^3 - 1 = 3(1^2 + 2^2 + \cdots +n^2) + 3(1 + 2 + \cdots +n) + n \]

\[n^3+3n^2+3n = 3(1^2 + 2^2 + \cdots +n^2) + 3\frac{n(1 +n)}{2} + n \]

整理后得

\[\sum_{i=0}^n i^2 = \frac{n (n+1)(2n+1)}{6} \]

首先和 \(f\) 一样,对其取模(根据引理 1. 可将其展开)。

\[\begin{split} g(a, b, c, n) &= \sum_{i=0}^n i\left\lfloor\frac{ai + b}{c}\right\rfloor \\ &= \sum_{i=0}^n i\left\lfloor\frac{(\left \lfloor \frac{a}{c}\right \rfloor c + a \bmod c)i + (\left \lfloor \frac{b}{c}\right \rfloor c + b \bmod c)}{c}\right\rfloor \\ &= \frac{n(n + 1)(2n+1)}{6} \left \lfloor \frac{a}{c}\right \rfloor + \frac{n(n + 1)}{2} \left \lfloor \frac{b}{c}\right \rfloor + g(a \bmod c , b\bmod c, c, n) \end{split} \]

其他部分推导与 \(f\) 类似。

\[S(i)={\left\lfloor\frac{ai + b}{c}\right\rfloor} - 1 \]

\[\begin {split} g(a, b, c, n) &= \sum_{j=0}^{S(n)} \sum_{i=0}^n i\left[ j \leq S(i) \right] \\ &= \sum_{j=0}^{S(n)} \sum_{i=0}^n i\left[ i > \left \lfloor \frac{jc + c - b - 1}{a} \right \rfloor\right] \\ \end {split} \]

\[t(j) =\left \lfloor \frac{jc + c - b - 1}{a} \right \rfloor \]

则有

\[\begin{split} g(a, b, c, n) &= \sum_{j=0}^{S(n)} \left((t(j) + 1) + (t(j) + 2) + \cdots + n\right) \\ &= \sum_{j=0}^{S(n)} \left(\frac{(t(j)+1+n)\times(n-t(j))}{2}\right) \\ &= \frac{1}{2}\left[ (S(n) + 1) n(n+1) - \sum_{j=0}^{S(n)}(t(j))^2 - \sum_{j=0}^{S(n)}t(j)\right] \\ &= \frac{1}{2} \left[(S(n) + 1) n(n+1) - h(c, c - b - 1, a, S(n)) - f(c, c - b - 1, a, S(n))\right] \end {split} \]

推导 \(h\)

同样套路,先取模

\[\begin{split} h(a, b, c, n) &= \sum_{i=0}^n \left\lfloor\frac{ai + b}{c}\right\rfloor^2 \\ &= \sum_{i=0}^n \left\lfloor\frac{(\left \lfloor \frac{a}{c}\right \rfloor c + a \bmod c)i + (\left \lfloor \frac{b}{c}\right \rfloor c + b \bmod c)}{c}\right\rfloor^2 \\ &= \sum_{i=0}^n \left(i \left \lfloor \frac{a}{c}\right \rfloor + \left \lfloor \frac{b}{c}\right \rfloor + \frac{(a \bmod c)i + (b\bmod c)}{c} \right)^2 \\ \end{split} \]

\[Q(i)=\frac{(a \bmod c)i + (b\bmod c)}{c} \]

拆开则有

\[\begin {split} h(a, b, c, n) &= \sum_{i=0}^n \left(i^2\left \lfloor \frac{a}{c}\right \rfloor^2 + \left \lfloor \frac{b}{c}\right \rfloor^2 + (Q(i))^2 + 2 i\left \lfloor \frac{a}{c}\right \rfloor \left \lfloor \frac{b}{c}\right \rfloor + 2i\left \lfloor \frac{a}{c}\right \rfloor Q(i) + 2\left \lfloor \frac{b}{c}\right \rfloor Q(i)\right) \\ &= \frac{n(n + 1)(2n + 1)}{6} \left \lfloor \frac{a}{c}\right \rfloor^2 + (n + 1)\left \lfloor \frac{b}{c}\right \rfloor^2 + h(a \bmod c, b \bmod c, c, n) + \\ & n(n+1)\left \lfloor \frac{a}{c}\right \rfloor \left \lfloor \frac{b}{c}\right \rfloor + 2\left \lfloor \frac{a}{c}\right \rfloor g(a \bmod c, b \bmod c, c, n) + 2\left \lfloor \frac{b}{c}\right \rfloor f(a \bmod c, b \bmod c, c, n) \end {split} \]

照样令

\[S(i)={\left\lfloor\frac{ai + b}{c}\right\rfloor} - 1 \\ t(j) =\left \lfloor \frac{jc + c - b - 1}{a} \right \rfloor \]

发现平方不好处理,将平方转化为加法

\[\begin{split} n^2 &= 2\frac{n(n + 1)}{2} - n \\ &= \left(2\sum_{i=0}^n i \right) - n \end{split} \]

\[\begin{split} h(a, b, c, n) &= \sum_{i=0}^n \left\lfloor\frac{ai + b}{c}\right\rfloor^2 \\ &= \sum_{i=0}^n \left[\left(2\sum_{j=0}^{S(i) + 1} j \right) - \left\lfloor\frac{ai + b}{c}\right\rfloor\right] \\ &= \left(2 \sum_{i=0}^n \sum_{j=0} ^{S(i) + 1} j\right) - f(a, b, c, n) \end{split} \]

现在只要解决前面的式子即可

\[\begin{split} \sum_{i=0}^n \sum_{j=0} ^{S(i) + 1} j &= \sum_{i=0}^n \sum_{j=0} ^{S(i)} (j+1) \\ &= \sum_{j=0}^{S(n)} (j+1)\sum_{i=0}^n \left[j < \left\lfloor\frac{ai + b}{c}\right\rfloor \right] \\ &= \sum_{j=0}^{S(n)} (j+1) (n - t(j)) \\ &= (S(n) + 1)n + \frac{S(n)(S(n) + 1)}{2} n - g(c, c - b - 1, a, S(n)) - f(c, c - b - 1, a, S(n)) \end{split} \]

综上所述

\[h(a, b, c, n) =(S(n)+1)(S(n) + 2) n - 2g(c, c - b - 1, a, S(n)) - 2f(c, c - b - 1, a, S(n)) - f(a, b, c, n) \]

\(a=0\) 时,上述三个式子都可以 \(\mathcal O(1)\) 计算。

代码实现时,因为三个函数交错递归,考虑三个一起整体递归,同步计算,时间复杂度为 \(\mathcal O(\log n)\)

Luogu5170

#include 
const int mod = 998244353;
int InvTwo, InvSix;
inline int read()
{
    int cnt = 0; char ch = getchar(); bool op = 1;
    for (; ! isdigit(ch); ch = getchar())
        if (ch == '-') op = 0;
    for (; isdigit(ch); ch = getchar())
        cnt = cnt * 10 + ch - 48;
    return op ? cnt : - cnt;
}

inline int quick_pow(int a, int b)
{
    int ret = 1;
    for (; b; b >>= 1)
    {
        if (b & 1) ret = 1ll * ret * a % mod;
        a = 1ll * a * a % mod;
    }
    return ret % mod;
}

struct eu
{
    int f, g, h;
};

inline int S(int a, int b, int c, int i)
{
    return ((1ll * a * i + b) / c) - 1;
}

inline eu solve(int a, int b, int c, int n)
{
    if (a == 0)
    {
        eu now = {0, 0, 0};
        now.f = 1ll * (b / c) * (n + 1) % mod;
        now.h = 1ll * (b / c) * (b / c) % mod * (n + 1) % mod;
        now.g = 1ll * n * (n + 1) % mod * InvTwo % mod * (b / c) % mod;
        return now;
    }
    if (a >= c || b >= c)
    {
        eu now = {0, 0, 0};
        now.f = (now.f + 1ll * n * (n + 1) % mod * InvTwo % mod * (a / c) % mod) % mod;
        now.f = (now.f + 1ll * (n + 1) * (b / c) % mod) % mod;
        now.g = (now.g + 1ll * n * (n + 1) % mod * (2 * n + 1) % mod * InvSix % mod * (a / c) % mod) % mod;
        now.g = (now.g + 1ll * n * (n + 1) % mod * InvTwo % mod * (b / c) % mod) % mod;
        now.h = (now.h + 1ll * n * (n + 1) % mod * (2 * n + 1) % mod * InvSix % mod * (a / c) % mod * (a / c) % mod) % mod;
        now.h = (now.h + 1ll * (n + 1) * (b / c) % mod * (b / c) % mod) % mod;
        now.h = (now.h + 1ll * n * (n + 1) % mod * (a / c) % mod * (b / c) % mod) % mod;
        eu nxt = solve(a % c, b % c, c, n);
        now.f = (now.f + nxt.f) % mod;
        now.g = (now.g + nxt.g) % mod;
        now.h = (now.h + nxt.h) % mod;
        now.h = (now.h + 1ll * 2 * (a / c) % mod * nxt.g % mod) % mod;
        now.h = (now.h + 1ll * 2 * (b / c) % mod * nxt.f % mod) % mod;
        return now;
    }
    else
    {
        eu now = {0, 0, 0};
        eu nxt = solve(c, c - b - 1, a, S(a, b, c, n));
        now.f = 1ll * (S(a, b, c, n) + 1) * n % mod;
        now.f = (now.f - nxt.f) % mod;
        now.f = (now.f + mod) % mod;
        now.g = 1ll * InvTwo * ((((1ll * (S(a, b, c, n) + 1) * n % mod * (n + 1) % mod - nxt.h) % mod) - nxt.f) % mod) % mod;
        now.g = (now.g + mod) % mod;
        now.h = (1ll * (S(a, b, c, n) + 1) * (S(a, b, c, n) + 2) % mod * n % mod) % mod;
        now.h = (now.h - 1ll * 2 * nxt.g) % mod;
        now.h = (now.h - 1ll * 2 * nxt.f) % mod;
        now.h = (now.h - now.f) % mod;
        now.h = (now.h + mod) % mod;
        return now;
    }
}

int main()
{
    int t = read();
    InvTwo = quick_pow(2, mod - 2);
    InvSix = quick_pow(6, mod - 2);
    while (t --)
    {
        int n, a, b, c;
        n = read(), a = read(), b = read(), c = read();
        eu ans = solve(a, b, c, n);
        printf("%d %d %d\n", ans.f, ans.h, ans.g);
    }
    return 0;
}

Reference

oi-wiki