Post

ABC315G - Ai + Bj + Ck = X (1 <= i, j, k <= N)

ABC315G - Ai + Bj + Ck = X (1 <= i, j, k <= N)

exgcd,即扩展欧几里得算法(Extended Euclidean Algorithm),是欧几里得算法的一个扩展版本。它用于求解两个整数a和b的最大公约数(Greatest Common Divisor,简称GCD),并且可以同时计算出满足贝祖等式 ax + by = gcd(a, b) 的整数解x和y。

题目大意

给定正整数 $N,A,B,C,X$ ,求满足以下要求的三元组 $(i,j,k)$ 的个数。

  • $1 \le i,j,k \le N$
  • $Ai+Bj+Ck=X$

思路

最暴力的做法是三重循环枚举 $i,j,k$,时间复杂度 $O(N^3)$。

优化一下,可以枚举 $i,j$,检查对应唯一的 $k$ 是否合法,时间复杂度 $O(N^2)$;

再换一个角度考虑,枚举每一个 $i$,并求出相对应的二元一次方程的合法解的数量,时间复杂度 $O(N \log N)$。这个过程可以通过使用 exgcd 求出方程的一个特解后,求出通解形式,再代入题目要求的不等式条件中求解。

实际上,由于 $B,C$ 是固定的,可以预处理好 $Bj+Ck=\gcd(B,C)$ 的特解,再在每个循环中 $O(1)$ 求出 $Bj+Ck=X-Ai$ 的特解(同时乘 $\frac{X-Ai}{gcd(B,C)}$),时间复杂度即为 $O(N)$。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
__int128 extend_gcd(__int128 a, __int128 b, __int128 &x, __int128 &y) {
    if (a == 0 && b == 0)
        return -1;
    if (b == 0) {
        x = 1, y = 0;
        return a;
    }
    __int128 d = extend_gcd(b, a % b, y, x);
    y -= a / b * x;
    return d;
}
__int128 ceil(__int128 x, __int128 y) {
    if (x % y == 0)
        return x / y;
    if (x < 0)
        return x / y;
    return x / y + 1;
}
__int128 floor(__int128 x, __int128 y) {
    if (x % y == 0)
        return x / y;
    if (x > 0)
        return x / y;
    return x / y - 1;
}
ll N, A, B, C, X, res;
int main() {
    ios;
    cin >> N >> A >> B >> C >> X;
    __int128 g = __gcd(B, C), j0, k0;
    B /= g, C /= g;
    extend_gcd(B, C, j0, k0);
    rep(i, 1, N) {
        __int128 x = X - A * i;
        if (x <= 0)
            break;
        if (x % g != 0)
            continue;
        else {
            x /= g;
            __int128 j = x * j0, k = x * k0;
            __int128 l[3], r[3];
            l[0] = ceil(1 - j, C);
            r[0] = floor((N - j), C);
            l[1] = ceil(k - N, B);
            r[1] = floor(k - 1, B);
            l[2] = max(l[0], l[1]), r[2] = min(r[0], r[1]);
            if (l[2] <= r[2])
                res += r[2] - l[2] + 1;
        }
    }
    cout << res;
    return 0;
}
This post is licensed under CC BY 4.0 by the author.