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,否则溢出
暴力枚举可能性 四重循环必超时;双重循环可过,但公式法更简洁

这样,一道原本看起来像几何计数的问题,就被转换成了简单的组合数学题目。希望这份题解能帮你理清思路!

posted @ 2026-05-10 22:16  炸酱面配小米酒  阅读(46)  评论(0)    收藏  举报