The 19th Zhejiang Provincial Collegiate Programming Contest - J. Frog 题解

cf 传送门


【大意】

在平面直角坐标系的原点上,放置一个半径为 \(1\) ,不可穿透的圆柱体。

一只青蛙需要从坐标 \((\cos ({d_s\over 180}\cdot \pi), \sin ({d_s\over 180}\cdot \pi))\) 处跳跃到 \((\cos ({d_t\over 180}\cdot \pi), \sin ({d_t\over 180}\cdot \pi))\) 处,且每一步的距离必须严格为 \(1\)

求最少步数,并输出方案。


【分析】

由于涉及到两个角度,我们不妨通过旋转,消除其中一个:将整个坐标系顺时针旋转 \(\theta_s={d_s\over 180}\cdot \pi\) 个弧度,最后计算答案的时候再逆回去,等价于左乘一个 \(M_0=\left(\begin{matrix}\cos\theta_s&-\sin\theta_s\\\sin\theta_s&\cos\theta_s\end{matrix}\right)\)

这下问题等价于从 \((1,0)\) 跳至 \((\cos ({d_t-d_s\over 180}\cdot \pi), \sin ({d_t-d_s\over 180}\cdot \pi))\) 处。

而题目要求步数最少,故当角度差值大于 \(180^\circ\) 时,答案等价于小于 \(180^\circ\) 的答案翻转得到。

故,若 \((d_t-d_s\bmod 360)>180\) 时,将 \(y\) 翻转,等价于先左乘一个 \(M_1=\left(\begin{matrix}1&0\\0&-1\end{matrix}\right)\),再左乘一个 \(M_0\) 。此时 \(M_0M_1=\left(\begin{matrix}\cos\theta_s&-\sin\theta_s\\-\sin\theta_s&-\cos\theta_s\end{matrix}\right)\)

现在,我们只要解决从 \((1, 0)\) 跳至 \((\cos\theta, \sin\theta)\) 处的问题即可,\(0\leq \theta\leq \pi\)。只要将过程中的每个点 \((x, y)\) 输出前,进行 \(M\) 的逆变换还原原坐标即可。


不难,对于当 \(\theta=0\) 的时候,步数为 \(0\)

而当 \(\theta>0\) 时,由于圆柱体不可跨越,不可能从圆柱边缘一点,跳跃至令一点。故不存在步数为 \(1\) 的解。

同时,为了保证跳跃过程中不会穿过圆柱,我们需要保证起始的方向起码要与圆柱相切(可以更外)。

很容易想到,当 \(\theta\leq {\pi\over 2}\) 时,存在路径 \((1, 0)\to(1+\cos\theta, \sin\theta)\to(\cos\theta, \sin\theta)\) ,长度为 \(2\)

再根据样例中 \(\theta=\pi\) 时需要 \(4\) 步,也很容易构造。对于 \(\theta>{\pi\over 2}\) 时,必然存在路径 \((1, 0)\to(1, 1)\to(0, 1)\to(\cos\theta, 1+\sin\theta)\to(\cos\theta, \sin\theta)\) ,长度为 \(4\)

剩下的问题就是,确认是否在 \(\theta>{\pi\over 2}\) 时,存在步数为 \(3\) 的路径。


直观上,我们会觉得,存在一条路径关于 \({\theta\over 2}\) 对称。

不妨假设路径如下所示:

根据图上几何关系,显然有 \(\theta+2\alpha+2\beta=3\pi, \theta\in({\pi\over 2}, \pi], \alpha\in[{\pi\over 2}, \pi]\)

于是有 \(\gamma=\theta-2\cdot {\pi-\alpha\over 2}=\theta+\alpha-\pi\) (等腰三角形性质)。

由于每条边都是 \(1\) ,对 \(\alpha\) 用余弦定理得 \(l^2=1^2+1^2-2\cdot 1\cdot 1\cdot \cos\alpha=2(1-\cos\alpha)\)

再对 \(\gamma\) 用余弦定理得 \(1^2=l^2+l^2-2\cdot l\cdot l\cdot \cos\gamma=2l^2(1-\cos\gamma)\)

代入整理得到 \((1-\cos\alpha)(1+\cos(\theta+\alpha))={1\over 4}\)

直观上我们发现,\(\alpha\) 越接近 \({\pi\over 2}\)\(\theta\) 越大。

故代入得到 \(\alpha={\pi\over 2}\) 时,\(\sin \theta={3\over 4}\) 。由于 \(\theta>{\pi\over 2}\) ,故 \(\theta=\pi-\arcsin{3\over 4}\approx 131.4^\circ\)

利用半角公式展开得到 \(\sin^2{\alpha\over 2}\cos^2{\theta+\alpha\over 2}={1\over 16}\)

注意到 \(\sin{\alpha\over 2}>0\wedge \cos{\theta+\alpha\over 2}<0\) ,故 \(\sin{\alpha\over 2}\cos{\theta+\alpha\over 2}=-{1\over 4}\)

再由积化和差公式得到 \(-{1\over 4}={1\over 2}(\sin(\alpha+{\theta\over 2})-\sin{\theta\over 2})\)

\(\sin(\alpha+{\theta\over 2})-\sin{\theta\over 2}=-{1\over 2}\)

由于 \(\alpha+{\theta\over 2}>{\pi\over 2}\) 。因此,对于确定的 \(\theta\) ,有 \(\alpha=\pi-\arcsin(\sin{\theta\over 2}-{1\over 2})-{\theta\over 2}\)

于是我们得到结论:当角度差值不超过 \(131\) 时,存在 \(3\) 步路径,否则为 \(4\) 步。

且第一步落脚点为 \((1+\cos(\pi-\alpha), \sin(\pi-\alpha))=(1-\cos\alpha, \sin\alpha)\)

第二步落脚点为 \((1-\cos\alpha, \sin\alpha)+(\cos(\pi-\beta+(\pi-\alpha)), \sin(\pi-\beta+(\pi-\alpha)))\)

\((1-\cos\alpha, \sin\alpha)+(\cos(\alpha+\beta), -\sin(\alpha+\beta))\)

其中,\(\beta={3\pi-\theta\over 2}-\alpha\)


【代码】

#include <bits/stdc++.h>
using namespace std;
#define de(x) cout << #x <<" = "<<x<<endl
#define dd(x) cout << #x <<" = "<<x<<" "
typedef long double db;
const db pi = acosl(-1);
const int lim=131;
db a11, a12, a21, a22;
inline void printxy(db x, db y) {
	cout<<a11*x+a12*y<<" "<<a21*x+a22*y<<"\n";
}
inline void work() {
	int ds, dt;
	cin>>ds>>dt;
	a11=cosl(pi*ds/180), a21=sinl(pi*ds/180), a12=-a21, a22=a11;
	dt=(dt-ds+360)%360;
	ds=0;
	if(dt>180) {
		a12=-a12;
		a22=-a22;
		dt=360-dt;
	}
	
	if(!dt) {
		cout<<"0\n";
		printxy(1, 0);
		return ;
	}
	db theta=dt*pi/180;
	
	if(dt<=90) {
		cout<<"2\n";
		printxy(1, 0);
		printxy(1+cosl(theta), sinl(theta));
		printxy(cosl(theta), sinl(theta));
		return ;
	}
	if(dt>lim) {
		cout<<"4\n";
		printxy(1, 0);
		printxy(1, 1);
		printxy(0, 1);
		printxy(cosl(theta), sinl(theta)+1);
		printxy(cosl(theta), sinl(theta));
		return ;
	}
	db alpha=pi-asinl(sinl(theta/2)-1.0/2)-theta/2;
	db beta=pi*3/2-alpha-theta/2;
	cout<<"3\n";
	db nowx=1, nowy=0;
	printxy(nowx, nowy);
	nowx+=-cosl(alpha);
	nowy+=sinl(alpha);
	printxy(nowx, nowy);
	nowx+=cosl(alpha+beta);
	nowy+=-sinl(alpha+beta);
	printxy(nowx, nowy);
	printxy(cosl(theta), sinl(theta));
}
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	cout<<fixed<<setprecision(9);
	int T; cin>>T;
	while(T--)
		work();
	cout.flush();
	return 0;
}
posted @ 2022-05-25 22:59  JustinRochester  阅读(145)  评论(0编辑  收藏  举报