P5088 矩形

· · 题解

思路

将原图逆时针旋转 90 \degree 并建系。

考虑到直接模拟光路较为复杂,我们可以建立如图所示的镜像单元格。

容易证明,图中同色线段长度相等。

于是问题就转换为:求直线第一次到达形如 (k_1m,k_2n) 的点(其中 k_1,k_2 \in \mathbb N_+)途中穿过矩形边的次数。

由于 \cot \zeta = \displaystyle{\frac{a}{b}},因此直线的解析式为 y = \displaystyle{\frac{a}{b}} \cdot x

设直线第一次到达的点为 (k_1m,k_2n),则:

\begin{aligned} \displaystyle{\frac{a}{b}} \cdot k_1m&=k_2n\\ amk_1&=bnk_2\\ k_1&=\displaystyle{\frac{bnk_2}{am}}. \end{aligned}

由于 k_1 \in \mathbb N_+,故 am \mid bnk_2

容易证明,k_2 的最小值为 \displaystyle{\frac{am}{\gcd(am,bn)}},此时 k_1=\displaystyle{\frac{bn}{\gcd(am,bn)}}

我们回到 k_1k_2 的定义,不难得出最终答案为 k_1+k_2-2

注意以下两点:

代码

#include<bits/stdc++.h>
#define int long long

using namespace std;

int n,m,a,b;

inline int read(){
    int ret=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')ret=ret*10+(ch&15),ch=getchar();
    return ret*f;
}

int gcd(int x,int y){
    return y?gcd(y,x%y):x;
}

int solve(int n,int m,int a,int b){
    if(!a||!b)return 0;
    int g1=gcd(n,m),g2=gcd(a,b);
    n/=g1,m/=g1,a/=g2,b/=g2;
    int g=gcd(a*m,b*n);
    int k1=b*n/g,k2=a*m/g;
    return k1+k2-2;
}

signed main(){
    n=read(),m=read(),a=read(),b=read();
    printf("%lld\n",solve(n,m,a,b));
    return 0;
}