本站在允许 JavaScript 运行的环境下浏览效果更佳

题解分享:[AtCoder Beginner Contest 414 E] Count A%B=C

题目链接:E - Count A%B=C
首发:题解:AT_abc414_e [ABC414E] Count A%B=C - 洛谷专栏
相关知识点:数论分块 - OI Wiki
附录:第一次参加 abc,100 分钟出了 ABCE 题。还得多多努力,大佬 40 分钟除了 G 都出了。

题目简述

给定正整数 N,统计满足以下条件的三元组 (a, b, c) 的个数:

  • 1 \leq a, b, c \leq N
  • a, b, c 两两不同
  • a \bmod b = c

输出方案数对 998244353 取模。

分析题目

对于每一组 (a, b)c = a \bmod b 唯一确定。
但三元组需满足两两不同,且 c \in [1, N]

所以完成以下三步

  1. 确定 b 的范围。
  2. 对于每一个 b,计算满足条件的 a 的取值个数。
  3. 枚举所有可能的 b,将每个 b 对应的合法 a 的取值个数累加,最后对答案取模后输出。

解题思路

确定 b 的范围

a,b 大小关系进行分类讨论:

  • a < ba \bmod b = a,此时 c = a,不符“两两不同”,排除。
  • a = ba \bmod b = 0,此时 c = 0,不在 [1, N],排除。
  • a > ba \bmod b = c,此时 N \geq a > b > c \geq 0,三者必然不同。

因此,只需考虑 a > b 的情况,N \geq a > b > c \geq 0 的情况

N \geq a > b > c \geq 0 与题目限制 c \geq 1,可得 b 的取值范围为 [2,N-1]

计算满足条件的 a 的取值个数

N \geq a > b > c \geq 0 可得:对于每个 ba 的取值范围为 [b+1, N],共 N-b 个。

但还需满足 c = a \bmod b \geq 1,即 a 不是 b 的倍数(否则 c=0 不合法)。

a 的取值范围 [b+1, N] 内,ab 的倍数的情况可以用等式表示:

a = k \times b ( k \in \mathbb{Z},\ 2 \leq k \leq \left\lfloor \frac{N}{b} \right\rfloor)

注释:因为 a > b 所以 k \neq 1

从上诉等式可知,a 可以取的倍数个数为 k \in [2,\lfloor \frac{N}{b}\rfloor]
即,ab 的倍数的个数为 \left\lfloor \frac{N}{b} \right\rfloor - 1

因此,对于每个 b,合法 a 的个数为:

(N-b) - (\left\lfloor \frac{N}{b} \right\rfloor - 1)

(N-b+1) - \left\lfloor \frac{N}{b} \right\rfloor

最终累加

综上所述,我们只需枚举 b 的所有可能取值 2 \leq b \leq N-1,对于每个 b,将其对应的合法 a 的个数 (N-b+1) - \left\lfloor \frac{N}{b} \right\rfloor 累加起来。

因此,答案可以表示为如下求和公式:

\sum_{b=2}^{N-1} \left[ (N-b+1) - \left\lfloor \frac{N}{b} \right\rfloor \right]

最后,将结果对 998244353 取模后输出即可。

代码实现

n = int(input())
mod = 998244353

ans = 0
for b in range(2, n):  # 2 <= b <= n-1
    ans += (n-b+1) - n//b
print(ans % mod)

复杂度分析

时间复杂度 O(N),题目 N 范围 3 \leq N \leq 10^{12},显然会超时。

优化:数论分块

分析求和公式

\begin{align*} &\sum_{b=2}^{N-1} \left[ (N-b+1) - \left\lfloor \frac{N}{b} \right\rfloor \right] \\ &= \underbrace{\sum_{b=2}^{N-1} (N-b+1)}_{\text{等差数列部分}} - \underbrace{\sum_{b=2}^{N-1} \left\lfloor \frac{N}{b} \right\rfloor}_{\text{整除分块部分}} \end{align*}
  • \sum_{b=2}^{N-1} (N-b+1) 部分是一个等差数列求和,可以直接用公式计算。
  • -\sum_{b=2}^{N-1} \left\lfloor \frac{N}{b} \right\rfloor 部分则可以通过分块优化高效计算。

关键思路

等差数列部分

由于 b2N-1(N-b+1) 形成一个首项为 N-1,末项为 2,共 N-2 项的等差数列。可以直接用等差数列求和公式计算:

\sum_{b=2}^{N-1} (N-b+1) = \frac{(N-2) \times (N+1)}{2}

整除分块部分

分块的核心思想

观察到 \left\lfloor \frac{N}{b} \right\rfloorb 的某些连续区间内取值是相同的。我们可以按照 \left\lfloor \frac{N}{b} \right\rfloor 的值 kb \in [2, N-1] 进行分块。对于每一个 k,计算对应区间内 b 的长度,然后将区间长度与该 k 的贡献相乘,累加到答案中。

如何找到每个分块的区间

设当前分块的起点为 left,此时 \left\lfloor \frac{N}{left} \right\rfloor = k。我们希望找到下一个分块的起点 LL > left),使得 \left\lfloor \frac{N}{L} \right\rfloor < k,并且 L 尽可能小。
用数学式表示为:

L = \min \left\{\, x \mid x > left,\;\left\lfloor \frac{N}{x} \right\rfloor < k \,\right\}

进一步推导,\left\lfloor \frac{N}{L} \right\rfloor < k \implies N < k \times L \implies L > \frac{N}{k},所以 L 取最小的满足 L > \frac{N}{k} 的整数,即

L = \left\lfloor \frac{N}{k} \right\rfloor + 1

最终,为了让分块区间尾不超过 N-1(即下一个分块区间头不超过 N),实际取 L = \min\left(\left\lfloor \frac{N}{k} \right\rfloor + 1,\, N\right)

每个分块的贡献

b \in [left, L-1] 这个区间内,\left\lfloor \frac{N}{b} \right\rfloor 的值都等于 \left\lfloor \frac{N}{left} \right\rfloor,区间长度为 L - left,所以这一段的总贡献为 (L - left) \times \left\lfloor \frac{N}{left} \right\rfloor

循环推进

每次处理完一个分块后,将 left 更新为 L,继续处理下一个分块。这个过程一直进行,直到分块的起点 left 超过 N-1,即循环条件为 left < N

最终实现

n = int(input())
mod = 998244353

ans = 0

# 计算等差数列和部分:(n-b+1) 从 b=2 到 b=n-1
# 即首项为 n-1,末项为 2,共 n-2 项
ans += (n - 2) * (n + 1) // 2

# 分块计算 n//b 的和
# 分块区间在 [2, n-1],注意边界处理
left = 2  # 当前分块区间的起点
while left < n:
    k = n // left  # 当前分块内 n//b 的值
    L = min(n // k + 1, n)  # 下一个分块区间的起点,当前分块区间的终点+1,不能超过 n
    ans -= (L - left) * k   # 区间长度*贡献
    left = L                # 移动到下一个分块的起点

print(ans % mod)

0
上一篇 中文博客圈列表
下一篇 题解分享:[蓝桥杯 2025 国 Python A] 杨辉三角 P12876