P2241 统计方形(数据加强版)
P2241 统计方形(数据加强版)题解
题目大意
有一个 \(n \times m\) 的方格棋盘,求其中包含的正方形和长方形(不包含正方形)的数量。$ n, m \le 5000$。
关键理解:一个矩形不是正方形就是长方形,所以:
\[\text{长方形数} = \text{矩形总数} - \text{正方形数}
\]
只要求出矩形总数和正方形数,题目就解决了。
一、数学公式推导
1. 矩形总数怎么算?
矩形的四条边:两条横边(上、下)和两条竖边(左、右)。
- 横边:棋盘有 \(n+1\) 条横线,选 2 条作为上下边,组合数是 \(C_{n+1}^{2} = \frac{n(n+1)}{2}\) 种选法。
- 竖边:棋盘有 \(m+1\) 条竖线,选 2 条作为左右边,组合数是 \(C_{m+1}^{2} = \frac{m(m+1)}{2}\) 种选法。
所有可能的横边选法和竖边选法都能组成唯一矩形,所以:
\[\text{矩形总数} = \frac{n(n+1)}{2} \times \frac{m(m+1)}{2}
\]
2. 正方形数怎么算?
设正方形边长为 \(k\)。
- 它在水平方向上有 \(m - k + 1\) 种移动位置。
- 在竖直方向上有 \(n - k + 1\) 种移动位置。
- \(k\) 的取值范围是从 \(1\) 到 \(\min(n,m)\)。
所以正方形总数就是:
\[\text{正方形数} = \sum_{k=1}^{\min(n,m)} (n - k + 1)(m - k + 1)
\]
二、为什么可以直接用公式 + 循环?
- 矩形总数直接用 \(O(1)\) 公式算,不耗时。
- 正方形数需要一个从 \(1\) 到 \(\min(n,m)\) 的累加循环(最多 5000 步),在 \(O(\min(n,m))\) 时间内完成。
- 完全不需要四重循环去枚举每个矩形的两个对角坐标(会超时)。
三、方法一:枚举边长法(C++)
思路
遍历正方形的可能边长 \(i\)(\(1\) 到 \(\min(n, m)\)),累加正方形数;矩形总数用组合公式直接算。
实现代码
#include <iostream>
using namespace std;
int main() {
long long n, m;
cin >> n >> m;
// 矩形总数 = C(n+1, 2) * C(m+1, 2)
long long total = n * (n + 1) / 2 * (m * (m + 1) / 2);
// 正方形数累加
long long sqr = 0;
for (int i = 1; i <= min(n, m); ++i) {
sqr += (n - i + 1) * (m - i + 1);
}
long long rec = total - sqr;
cout << sqr << " " << rec << endl;
return 0;
}
四、方法二:公式拓展法(C++)
思路
公式 \(\sum_{k=1}^{t} (n - k + 1)(m - k + 1)\) 可以展开成关于 \(t = \min(n,m)\) 的多项式,直接算出 \(O(1)\)。具体过程是:
- 正方形数公式展开得:
\[\sum_{k=1}^{t} (n - k + 1)(m - k + 1)
= t \cdot (n+1)(m+1) - (n + m + 2) \sum_{k=1}^{t} k + \sum_{k=1}^{t} k^2
\]
- 将 \(\sum k = \frac{t(t+1)}{2}\) 和 \(\sum k^2 = \frac{t(t+1)(2t+1)}{6}\) 代入即可。
这样做能得到一个 \(O(1)\) 解法,但代码可能较长,这里不再给出完整实现。如果遇到 \(n,m\) 大很多倍(如 \(10^9\))的情况,可以用这个思路。
五、Python 实现(适合小白)
n, m = map(int, input().split())
# 矩形总数
total = n * (n + 1) // 2 * m * (m + 1) // 2
t = min(n, m)
sqr = 0
for i in range(1, t + 1):
sqr += (n - i + 1) * (m - i + 1)
print(sqr, total - sqr)
运行结果与 C++ 一模一样。
六、为什么必须用 long long?
- 矩形总数公式中,最大 \(n = m = 5000\) 时,矩形总数约为:
\[\frac{5000 \times 5001}{2} \times \frac{5000 \times 5001}{2} \approx 1.56 \times 10^{14}
\]
- 这个数已经超过
int的范围(\(< 2.1\times 10^9\)),必须用 64 位整型long long(C++)或 Python 的默认大整数。
总结笔记
| 知识点 | 具体内容 |
|---|---|
| 核心公式 | 矩形总数 \(= \frac{n(n+1)}{2} \times \frac{m(m+1)}{2}\) |
| 正方形数 | \(\sum_{k=1}^{\min(n,m)} (n - k + 1)(m - k + 1)\) |
| 长方形数 | 矩形总数 \(-\) 正方形数 |
| 时间复杂度 | \(O(\min(n,m))\),可优化到 \(O(1)\) |
| 数据类型 | C++ 必须使用 long long,否则溢出 |
| 暴力枚举可能性 | 四重循环必超时;双重循环可过,但公式法更简洁 |
这样,一道原本看起来像几何计数的问题,就被转换成了简单的组合数学题目。希望这份题解能帮你理清思路!

浙公网安备 33010602011771号