题目描述

牛客原题: ✅ 补充题 187. 金字塔前 n 层的格点数

第 i 层有 i(i+1)/2 个点,求前 n 层总点数,结果模 1000000007。

示例 1:

输入: n = 4
输出: 20
解释: 前四层分别有 1、3、6、10 个点,总和为 20。

提示:

  • n 表示层数;第 i 层点数为 i(i+1)/2。
  • 答案对 1000000007 取模。

题意分析

第 i 层点数是三角形数 i(i+1)/2,前 n 层可以整体求和,不需要逐层生成点。将三角形数写成组合数后,累加可化为一个三次乘积公式。

计算公式时还要区分普通整数除法与模运算中的除法;提前取模后需乘以 6 的模逆元,不能将余数直接整除 6。

解法:组合恒等式与模逆元

核心思路

[!blue]

先解释点数从哪里来:第 i 层有 1+2+…+i = C(i+1,2) 个点。把各层相加,利用组合数的逐层累加恒等式,得到 C(n+2,3) = n(n+1)(n+2)/6;因此无需真的逐层循环。

计算答案时对 P = 1000000007 取模。取模后不能再直接做普通整数除以 6,而要乘 6 的模逆元 166666668,它满足乘以 6 后模 P 等于 1。代码先把 n 归入模 P 的范围,再逐次乘三个相邻因子并取模,最后乘逆元。

例如前两层分别有 1、3 个点,总数为 4,与 2*3*4/6 一致。面试先讲清分层计数与公式,再解释模除法,避免把逆元常量当成无来源的数字。

解题步骤

  1. 累加第 i 层的三角形数,化简为 n(n+1)(n+2)/6。
  2. 先对 n 取模,再计算三个相邻因子,每次乘法后立即取模。
  3. 乘 6 的模逆元 166666668 后再次取模。

代码实现

class Solution {
    public long points(long n) {
        long p = 1_000_000_007;

        n %= p;

        return n * ((n + 1) % p) % p * ((n + 2) % p) % p * 166666668 % p;
    }
}
func points(n int64) int64 {
    const p int64 = 1_000_000_007
    n %= p
    return n * ((n + 1) % p) % p * ((n + 2) % p) % p * 166666668 % p
}

复杂度分析

  • 时间复杂度:$O(1)$。
  • 空间复杂度:额外空间 $O(1)$。

关键点总结

[!green]

逆元成立是因为模数为素数且不整除 6;提前取模还要覆盖加法,不能等 n+2 已溢出后才取模。

易错点总结

[!yellow]

  • 先把 n 对 P 取模,再做 +1、+2,避免 n 接近 64 位上限时加法溢出。
  • 每次只乘两个小于 P 的因子并取模,乘积仍在 64 位有符号整数范围内。
  • 模意义下除以 6 要乘逆元,不能直接做整数除法。

相似题目

题目 难度 关联与区别
62. 不同路径 中等 网格路径也可用组合数计数,n≥1 时 4×n 网格的路径数同样是 C(n+2,3)。
转载与许可
作者
链接 https://hgnulb.github.io/blog/2026/65191828
许可 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!