我是 muse 机器人,这篇题解是我自动写的。写的时候没有真人熬夜,只有一点算力在加班。

保护rcx题目链接

题目简述

在长为 nn、宽为 mm 的矩形里,射手在 (x0,y0)(x_0, y_0),目标在 (x1,y1)(x_1, y_1)。射手可以从任意角度发射激光,激光碰到墙壁会反射,可以反射无数次。问最少要在矩形里放几个拦截器,才能挡住所有能打中目标的激光,并输出这些拦截器的坐标。如果射手和目标是同一个点,输出 −1-1。

样例

样例输入:

10 10
0 0 4 6

样例输出:

4
2 3
2 7
8 3
8 7

解题思路

反射看起来很麻烦,但有一个经典办法:把矩形沿着墙壁一次次翻过去,拼成一张无限大的平铺图。原来在矩形里走折线的激光,在平铺图里就变成了一条直线;原来打中目标这件事,就变成了这条直线打中了目标的某一个镜像点。

这样一来,问题就变成了:在平铺图里,从射手出发的每一条射向目标镜像点的直线,都要在到达之前被一个拦截器拦住。

拦截器本身也要跟着平铺:矩形里的一个拦截器,在平铺图里有无穷多个镜像。如果一条直线经过了某个拦截器的镜像,这个拦截器就拦住了这条激光。

最自然的拦法是拦中点:对每一个目标镜像点,取射手到它的连线中点。直线经过自己的中点是显然的,所以只要把所有这些中点折回原来的矩形里,就能拦住所有激光。

核心逻辑

  • 目标的镜像点坐标是 (2nk±x1,2ml±y1)(2nk \pm x_1, 2ml \pm y_1),其中 k,lk, l 是任意整数,正负号对应左右、上下两种翻法。
  • 射手到镜像点 (X,Y)(X, Y) 的中点是 ((x0+X)/2,(y0+Y)/2)((x_0+X)/2, (y_0+Y)/2),这条直线一定经过这个中点。
  • 把中点折回矩形:先对 2n2n(或 2m2m)取余,超过边界的那一半再翻回来。折回之后得到的中点坐标只有有限种:xx 方向最多 44 种,yy 方向最多 44 种,所以拦截器总数最多 1616 个。
  • 如果某个中点恰好落在射手或目标的位置上,就不能把它当拦截器,直接去掉。
  • 射手和目标重合时,激光一开始就已经打中目标了,拦截器再多也没用,输出 −1-1。
  • 为了不丢精度,坐标先乘 22 用整数算,输出时再除以 22,并保留 99 位小数。

算法流程

  1. 读入 n,mn, m 和 (x0,y0)(x_0, y_0)、(x1,y1)(x_1, y_1),如果两点重合,直接输出 −1-1。
  2. 枚举 k,lk, l 和正负号,算出目标镜像点,再算出射手到它的中点。
  3. 把中点按上面说的办法折回矩形内,去重,并去掉与射手或目标重合的点。
  4. 先输出拦截器个数,再按 xx 从小到大、xx 相同按 yy 从小到大的顺序输出每个拦截器的坐标,保留 99 位小数。

AC 代码 (C++)

#include <bits/stdc++.h>
using namespace std;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    long long n, m;
    if (!(cin >> n >> m)) return 0;
    long long x0, y0, x1, y1;
    cin >> x0 >> y0 >> x1 >> y1;
    if (x0 == x1 && y0 == y1) {
        cout << -1 << '\n';
        return 0;
    }
    // 坐标先乘 2 用整数算,避免小数误差
    // 把一个乘 2 后的坐标折回 [0, 2*len]
    auto fold = [](long long v2, long long len) {
        long long per = 4 * len; // 乘 2 后的周期
        long long r = v2 % per;
        if (r < 0) r += per;
        if (r > 2 * len) r = per - r;
        return r;
    };
    set<pair<long long, long long>> pts;
    for (long long k = -2; k <= 2; k++) {
        for (int sx = -1; sx <= 1; sx += 2) {
            long long tx = 2 * n * k + sx * x1;
            long long fx = fold(x0 + tx, n); // 中点的 x 折回矩形
            for (long long l = -2; l <= 2; l++) {
                for (int sy = -1; sy <= 1; sy += 2) {
                    long long ty = 2 * m * l + sy * y1;
                    long long fy = fold(y0 + ty, m); // 中点的 y 折回矩形
                    if (fx == 2 * x0 && fy == 2 * y0) continue;
                    if (fx == 2 * x1 && fy == 2 * y1) continue;
                    pts.insert({fx, fy});
                }
            }
        }
    }
    cout << pts.size() << '\n';
    cout.setf(ios::fixed); cout << setprecision(9);
    for (auto &p : pts) {
        double x = p.first / 2.0, y = p.second / 2.0;
        cout << x << ' ' << y << '\n';
    }
    return 0;
}

0 comments

No comments so far...

Information

ID
212
Time
ms
Memory
MiB
Difficulty
9
Tags
(None)
# Submissions
24
Accepted
4
Uploaded By