Codeforces 724C - Ray Tracing
Published on 2016-10-10描述
一束激光从 出发,速度向量为 ,每秒行走 个单位。矩形以 为左下角, 为右上角,这一束激光只能在矩形内部传播,碰到矩形边缘则反射(遵循光的反射定律),如果传播到矩形的角则停止传播。
现在有 个传感器,第 个的坐标为 ,请问对于每个传感器,激光最早在什么时候经过它?输出最早的时间,若不经过,输出 -1
。
分析
这个题显然不好模拟,怎么办呢?在初中物理中有虚像这一概念,光线反射集中某个点,可以看做是光线直射到某个点关于反射面的对称点,我们利用这一性质来进行解题。
我们只对矩形的左右边缘处理虚像,如果上下边缘也处理的话,貌似就不太好做了。我们观察一下 的情况:
可以发现最后光线一定会打到角上,终点的横坐标是 。可以发现对于点 ,它的坐标变成了两种: 以及 ,其中都有 。无论是哪一种,我们只需要找到一个点,满足它在光线上,而且横坐标最小。有解的充要条件是横坐标不能超过 。
如何判断一个点是否在光线上面?假设某个点的形式为:,那么光线有两种,一种是上升的,此时可以列出方程:
对于下降的光线,也可以列出方程:
于是无论是上升的或者是下降的光线,我们都可以列出一个关于 的方程,我们只需要解出一个最小的 来,就能解出最小的横坐标,解方程可以采用扩展欧几里德算法,也可以预处理出 表示满足 的最小的 。
对于每个传感器 ,我们有四种情况,用 work(a, b)
表示求解方程 ,返回最小的横坐标 ,则四种情况都可以用 work(a, b)
一并求解。
如果采用扩展欧几里德算法解方程:时间复杂度:,不需要额外的空间。
如果采用预处理的方法解方程:时间复杂度:,额外空间消耗: 。
代码
// Created by Sengxian on 2016/10/10. // Copyright (c) 2016年 Sengxian. All rights reserved. // Codeforces 724C 同余方程 #include <bits/stdc++.h> using namespace std; typedef long long ll; inline int ReadInt() { static int n, ch; n = 0, ch = getchar(); while (!isdigit(ch)) ch = getchar(); while (isdigit(ch)) n = n * 10 + ch - '0', ch = getchar(); return n; } const int MAX_K = 100000 + 3, MAX_M = 100000 + 3; const ll INF = 0x3f3f3f3f3f3f3f3f; int n, m, k, N, M; ll vis[MAX_M * 2]; inline ll mod(ll a) { return (a % M + M) % M; } inline ll work(ll a, ll b) { //(kN + a + b) % M = 0 //(kN + a) is x coordinate a = a, b = b; ll t = mod(-(a + b)); if (vis[t] == -1) return INF; else return (ll)vis[t] * N + a; } int main() { #ifndef ONLINE_JUDGE freopen("test.in", "r", stdin); #endif n = ReadInt(), m = ReadInt(), k = ReadInt(); N = n * 2, M = m * 2; memset(vis, -1, sizeof vis); ll now = 0, cnt = 0; //注意这里所有的 vis[now] 都是合法的,因为 now 的实际值不超过 lcm(n, m) while (vis[now] == -1) { vis[now] = cnt++; (now += N) %= M; } for (int i = 0, x, y; i < k; ++i) { x = ReadInt(), y = ReadInt(); ll t = min(min(work(x, -y), work(x, y)), min(work(N - x, -y), work(N - x, y))); if (t != INF) printf("%lld\n", t); else puts("-1"); } return 0; }