题目描述
有两个建筑物,分别有 $n$ 和 $m$ 个窗户,你需要判断一个光源是否照到第一个建筑物的任何一个窗户,如果光线照到了,那么就反射到第二个建筑物,再判断这个光线是否照到了第二个建筑物的任何一个窗户。如果照到了,输出窗户号,并输出照射到的窗户数。
思路
对于这种照射问题,我们可以把它视为连线问题。从光源出发,递归反射,直到反射不到为止。因为两个建筑物都是平行的,所以我们可以把两个轴都作为平面来处理,建立平面直角坐标系。因为这个光源位置不在任何一个窗户的边界上,所以这个坐标系相对固定。
我们可以选择两种方式来计算两点间的连线是否与窗户相交,一种是扫描线算法,另一种是向量法,这里我们使用后者。
对于两个点 $(x_1, y_1), (x_2, y_2)$,可以定义它们的向量为 $\vec{v} = (x_2 - x_1, y_2 - y_1)$,假设光线与窗户分别相交于 $(a, b), (c, d)$,因为法线与反射线上任何一点的向量都相同,所以有 $\vec{v}\cdot\vec{n} = 0$,其中 $\vec{n}$ 为法线向量,其方向在 $(-v_2, v_1)$ 上。因为 $n_1$ 和 $n_2$ 均为 $\pm1$ 并且 $|n_1| + |n_2| = 1$,所以 $n_1$ 和 $n_2$ 中必定有一个为 $0$。因为 $\vec{v}$ 的坐标为整数,所以可以先对两个坐标进行差分,最后再把两个坐标乘上正负号即可,即
$$n_1 = \mathrm{sgn}(v_2),\quad n_2 = -\mathrm{sgn}(v_1)$$
然后,因为上面的法线只是粗略的法线,我们还要对它进行细化,做法是求出两个点的单位向量,然后与粗略法线向量相加,再除法线长度,即
$$\vec{u}_1 = \frac{1}{\sqrt{v_1^2 + v_2^2}}(v_1, v_2),\quad \vec{u}_2 = \frac{1}{\sqrt{v_1^2 + v_2^2}}(-v_2, v_1)$$
$$\vec{n} = (\vec{u}_1 + \vec{u}_2)/\sqrt{2}$$
然后,对于每个窗户,判断从光源到两个窗户的向量与粗略法线的向量积是否同号,如果不同,说明这个光线没有经过这扇窗户,否则递归调用反射函数即可。