题解:洛谷 P2997 Banner

【题目来源】

洛谷:P2997 [USACO10NOV] Banner S

【题目描述】

Bessie 刚从国外长途旅行回来,农夫约翰想在她的牧场里竖起一个漂亮的「欢迎回家」横幅。横幅将挂在两根柱子之间的电线上,电线的长度范围是 \(L_1..L_2\),其中 \(1 \le L_1 \le L_2\),且 \(L_1 \le L_2 \le 1,500\)

牧场的大小为 \(W \times H\),其中 \(1 \le W \le 1,000\)\(1 \le H \le 1,000\),农夫约翰在每个整数坐标点上都安装了一根柱子。在这些 \((W + 1) \times (H + 1)\) 个点中,农夫约翰必须选择两个点来固定电线的两端,以便悬挂横幅。

约翰希望横幅悬挂时不受干扰,并要求在他拉紧的电线下方没有柱子。

农夫约翰需要你的帮助来计算他有多少种可能的方式来悬挂横幅。他知道这个数量很大,32 位整数可能不足以计算出答案。

考虑下面的牧场示例,其中 \(W = 2\)\(H = 1\)

* * *
* * *
横幅的长度范围是 \(2..3\)。这个牧场包含 \((2+1) \times (1+1) = 6\) 个点,并且有 \(\binom{6}{2} = \frac{6\times5}{2\times1} = 15\) 对不同的点对,可以在其间拉伸横幅固定电线:

(0,0)-(0,1)   (0,0)-(2,1)   (0,1)-(2,1)   (1,1)-(2,0)
(0,0)-(1,0)   (0,1)-(1,0)   (1,0)-(1,1)   (1,1)-(2,1)
(0,0)-(1,1)   (0,1)-(1,1)   (1,0)-(2,0)   (2,0)-(2,1)
(0,0)-(2,0)   (0,1)-(2,0)   (1,0)-(2,1)

在这些点对中,只有四对的长度在 \(2..3\) 的范围内:
长度 长度

(0,0)-(2,0) 2.00 (0,1)-(2,0) 2.24

(0,0)-(2,1) 2.24 (0,1)-(2,1) 2.00

在这四对中,点对 (0,0)-(2,0) 和 (0,1)-(2,1) 的连线上都有柱子,因此不合适。

所以,在 15 对点中,只有两对是合适的悬挂横幅电线的候选者。

【输入】

* 第 1 行:四个用空格分隔的整数:\(W\)\(H\)\(L_1\)\(L_2\)

【输出】

* 第 1 行:一个整数,表示可能的横幅数量。

【输入样例】

2 1 2 3

【输出样例】

2

【核心思想】

  1. 问题分析:在一个 \((W+1) \times (H+1)\) 的整数点阵中,选择两个点作为电线端点,要求两点间距离在 \([L_1, L_2]\) 范围内,并且两点连线不经过任何其他整数点(即端点之间无其他柱子)。求满足条件的点对数量。本质是枚举所有可能的相对位移向量 \((i, j)\),并利用 \(i\)\(j\) 互质 来保证线段不经过中间整点。

  2. 算法选择

    • 枚举向量 + 互质判定:直接枚举两点在水平和垂直方向上的差值 \((i, j)\)\(i \in [0, W]\)\(j \in [0, H]\),排除 \(i=j=0\))。对于每个非零向量,其长度平方为 \(i^2 + j^2\),若在 \([L_1^2, L_2^2]\) 之间,且 \(\gcd(i, j) = 1\),则该向量对应的所有点对均合法。
    • 计数策略:对于固定的 \((i, j)\),在 \(W \times H\) 的网格中,水平方向有 \((W+1-i)\) 种起点列位置,垂直方向有 \((H+1-j)\) 种起点行位置,因此共有 \((W+1-i)(H+1-j)\)有序方向的点对。由于 \((i, j)\)\((-i, -j)\) 代表同一对点(但起点和终点互换),代码中通过乘以 \(2\) 来处理正负方向(当 \(i \neq 0\)\(j \neq 0\) 时)。
    • 特殊情况:水平相邻(\(i=1, j=0\))和垂直相邻(\(i=0, j=1\))的点对,如果长度在 \([L_1, L_2]\) 范围内(即 \(L_1 \le 1 \le L_2\)),需单独计数,因为代码中的双层循环从 \(i=1, j=1\) 开始,不包含 \(i=0\)\(j=0\) 的情况。
  3. 关键步骤

    • 读入\(W, H, L_1, L_2\)
    • 特判相邻点对:若 \(L_1 \le 1\)\(L_2 \ge 1\),则水平相邻点对数为 \(W \times (H+1)\),垂直相邻点对数为 \(H \times (W+1)\),累加至答案。
    • 枚举斜向量\(i\)\(1\)\(W\)\(j\)\(1\)\(H\)
      • 计算距离平方 \(t = i^2 + j^2\)
      • \(t < L_1^2\)\(t > L_2^2\),跳过。
      • \(\gcd(i, j) \neq 1\),跳过(连线经过中间整点)。
      • 若合法,累加 \(2 \times (W+1-i) \times (H+1-j)\) 到答案(两个方向)。
    • 输出答案
  4. 时间/空间复杂度

    • 时间复杂度:\(O(W \cdot H \cdot \log(\min(W, H)))\),其中 \(\log\) 来自 \(\gcd\) 计算,\(W, H \le 1000\),完全可行。
    • 空间复杂度:\(O(1)\)
  5. 枚举 + 互质判定的核心思想

    • 几何转换:将“两点间无其他柱子”等价为“水平和垂直位移互质”。因为线段经过的整数点个数为 \(\gcd(i, j) - 1\)(不包括端点),只有 \(\gcd(i, j) = 1\) 时,中间无整点。
    • 长度条件\(L_1 \le \sqrt{i^2+j^2} \le L_2\),为了避免浮点数误差,直接比较平方 \(L_1^2 \le i^2+j^2 \le L_2^2\)
    • 计数对称性:对于每个满足条件的 \((i, j)\),向量 \((i, -j)\) 也满足条件,且对应不同的点对,因此乘 \(2\)。对于水平或垂直向量,没有对称乘 \(2\) 的重复(因为方向相反的点对已被计入),所以需要单独处理。
    • 适用场景:适用于格点问题中统计“可见点对”(两整点之间不经过其他整点)的数量,核心是利用 \(\gcd\) 判定可见性。

【算法标签】

普及 #数学

【代码详解】

#include <bits/stdc++.h>
using namespace std;

#define int long long // 使用 long long 防止答案溢出

int w, h, l1, l2, ans; // 牧场宽、高,电线长度范围下限、上限,答案

// 求最大公约数,用于判断两点连线是否经过其他整点
int gcd(int a, int b)
{
    if (b == 0)
        return a;
    else
        return gcd(b, a % b);
}

signed main()
{
    cin >> w >> h >> l1 >> l2;

    // 特殊情况:长度范围为 1 时,处理水平或垂直的相邻柱子
    // 此时两点连线长度为 1,且不可能经过其他柱子,直接计数
    // 水平相邻点对:w 列间隙 * (h+1) 行
    // 垂直相邻点对:h 行间隙 * (w+1) 列
    if (l1 <= 1 && l2 >= 1)
        ans = w * (h + 1) + h * (w + 1);

    // 枚举两点在水平和垂直方向上的差值 (i, j)
    // i 为水平距离(列差),j 为垂直距离(行差),且均大于 0,代表斜向或非相邻情况
    for (int i = 1; i <= w; i++)
        for (int j = 1; j <= h; j++)
        {
            // 计算实际欧几里得距离的平方
            int t = i * i + j * j;

            // 若距离平方不在 [l1^2, l2^2] 范围内,则长度不合要求,跳过
            if (t > l2 * l2 || t < l1 * l1)
                continue;

            // 若 gcd(i, j) != 1,说明两点连线经过其他整数点(柱子),不合要求,跳过
            if (gcd(i, j) != 1)
                continue;

            // 统计所有满足条件的点对
            // 水平方向有 (w+1-i) 种起始列位置,垂直方向有 (h+1-j) 种起始行位置
            // 每个 (i, j) 向量对应两种方向(斜率为正和斜率为负),故乘以 2
            ans += 2 * (w + 1 - i) * (h + 1 - j);
        }

    cout << ans << endl;
    return 0;
}

【运行结果】

2 1 2 3
2
posted @ 2026-08-30 09:20  团爸讲算法  阅读(3)  评论(0)    收藏  举报