CF274E Mirror Room题解
题意
\(n \times m\) 的平面,有 \(k\) 个障碍物,激光碰到障碍物后会反射,给定激光初始位置和方向,求激光能经过多少障碍物。
解法
首先需要证明两个性质:
- 激光反弹次数为 \(O(n+m+k)\) 级别。
- 证明:对角线级别为 \(O(n+m)\),有 \(k\) 个障碍物把这些对角线分为 \(O(k)\) 份,总数为 \(O(n+m+k)\)。
- 同一个方格不会经过交叉激光。
- 证明:类似黑白染色,把相邻的染成不同颜色,然后发现,同一方向上的路径所经过的放格颜色总相同,因为每次转向一定会变换到相邻的颜色不同的格子。
根据第一个性质,可以想到快速找到下一次碰到障碍的位置,可以利用 set 存对角线上的点,每次 \(ans\) 累加移动距离,由于性质二,不会有交叉算重的情况,如果反向了怎么办?
处理方法:从当前点能够到达的某个边界(或障碍物附近)开始运行代码,过程中如果出现走反向边的情况标记一下,容易证明,一旦走到反向边,激光会按照原路返回,所以每个点会被恰好计算两次,除以二即可,如果没有经过反向,则每个点只会经过一次(因为既没有交叉,又没有反向),所以,每个点只会经过一次。
一些细节:边界也要算作障碍。
代码
// Problem: E. Mirror Room
// Contest: Codeforces - Codeforces Round #168 (Div. 1)
// URL: https://codeforces.com/problemset/problem/274/E
// Memory Limit: 256 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
#define int long long
using namespace std;
namespace Std {
int n, m, k, x, y, d, tx, ty, td, cs = 1, ans;
set<pair<int, int> > cset[2][200010];
unordered_map<int, int> cmap[100010];
const int dx[] = {1, -1, -1, 1}, dy[] = {-1, -1, 1, 1};
char s[5];
inline void add(int a, int b) {
cmap[a][b] = 1;
cset[0][a + b].insert(make_pair(a, b));
cset[1][b - a + n].insert(make_pair(a, b));
}
void work(bool opt) {
auto it = cset[d & 1][d & 1 ? y - x + n : x + y].lower_bound(make_pair(x, y));
if (d == 1 || d == 2) it--;
if (opt) ans += abs(x - (*it).first);
x = (*it).first - dx[d], y = (*it).second - dy[d];
int cnt = cmap[x + dx[d]].count(y) + cmap[x].count(y + dy[d]);
if ((!cnt) || cnt == 2) {
cs = 2;
d ^= 2;
} else if (cmap[x + dx[d]].count(y)) {
y += dy[d];
d ^= 1;
} else {
x += dx[d];
d ^= 3;
}
}
int main() {
scanf("%lld%lld%lld", &n, &m, &k);
int a, b;
for (int i = 1; i <= k; ++i) {
scanf("%lld%lld", &a, &b);
add(a, b);
}
for (int i = 1; i <= n; ++i) {
add(i, 0);
add(i, m + 1);
}
for (int i = 1; i <= m; ++i) {
add(0, i);
add(n + 1, i);
}
scanf("%lld%lld", &x, &y);
scanf("%s", s + 1);
if (s[1] == 'S') d += 2;
if ((s[1] == 'S' && s[2] == 'E') || (s[1] == 'N' && s[2] == 'W')) ++d;
add(0, 0);
add(0, m + 1);
add(n + 1, 0);
add(n + 1, m + 1);
work(false);
tx = x;
ty = y;
td = d;
work(true);
while (x != tx || y != ty || d != td) {
work(true);
}
printf("%lld\n", ans / cs);
return 0;
}
} // namespace Std
#undef int
int main() { return Std::main(); }
未经授权,禁止转载。