题意简述:在平面上有一个坐标 \((x_c,y_c)\) 和半径 \(r\) 都是整数的圆 \((1\leq r_c\leq \sqrt{x_c^2+y_c^2}-1)\),你可以询问不超过 \(60\) 次一个从原点出发的射线 \((0,0)\to (x,y)\) 与圆的距离(若相交,则返回 \(0\))。确定圆的坐标和半径。

\(|x_c|,|y_c|,r\le 10^5\),你需要保证 \(|x|,|y|\leq 10^6\)。

一道有趣的计算几何,思维含量并不是很高(*3300 有点离谱),就是写起来麻烦了些。比赛的时候花了四个小时写这题,结果发现前面两个半小时写的东西完全没有用(狂笑)。


Step 1:旋转坐标轴规避分类讨论

首先我们得确定这个圆的大致位置,也就是在哪个象限。因为如果对于四个象限分别分类讨论很麻烦,不如旋转坐标轴使得这个圆落在某些固定的象限,每次旋转 \(90^{\circ}\)。不妨将圆心旋转到 \(x\) 轴上方,且确保这个圆不经过第三象限,这是可以同时做到的:

我们可以通过询问 \((0,\pm i)\) 与 \((\pm i,0)\) 来确定圆和坐标轴是否有交。

  • 若没有交点,将坐标轴旋转使得圆落在第一象限。
  • 若有一个交点,将坐标轴旋转使得圆落在第一、二象限。
  • 若有两个交点,将坐标轴旋转使得圆落在第一、二、四象限。

不要忘了旋转后询问要转回来。比如我们将坐标轴顺时针旋转 \(90^{\circ}\) 后,询问时需要将坐标逆时针旋转 \(90^{\circ}\)。


Step 2:二分找到与圆相切的射线

由于圆心在 \(x\) 轴上方,询问交互库 \((0,x)\ (x<0)\) 的返回值 \(d_0\) 显然是 \(\sqrt{x_c^2+y_c^2}-r\):因为过圆心且垂直于射线的直线与射线没有交点,故最短距离为原点与圆心距离减去半径。

注意到根据三个方程我们可以直接解出 \((x,y,r)\),\(d_0=\sqrt{x_c^2+y_c^2}-r\) 已经是其中一个了。只需要再找到两个和圆距离 \(\neq d_0\) 且 \(\neq 0\) 的射线即可,称其为合法射线

但是我们不能随便询问!因为可以构造一个与 \(y=kx\) 非常相近且半径非常大的圆,例如与 \(y=-x\) 几乎相切的 \((50000,50000,70709)\),使得合法的射线范围非常小。因为一旦稍有偏移,我们询问到的是 \(d_0\),要么就是 \(0\)(上述例子中,只有 \(y=-x\) 和过原点的圆的两条切线之间所夹住的区域合法)。而我们不知道出题人会构造什么毒瘤数据,这也意味着我们不能询问固定的射线,这样是没有前途的。

但注意到距离切线非常近(指与切线夹角非常小且与圆相离的射线范围)的那一块区域是合法的,这启发我们二分出几乎与圆相切(但与圆相离)的两条射线 \(a:(0,0)\to (x_a,y_a)\) 与 \(b:(0,0)\to (x_b,y_b)\)。具体怎么二分呢,想象一个 \((2\times10^6)\times (2\times 10^6)\) 的盒子包住了整个坐标轴。我们在从左逼近时,若返回值 \(>\rm eps\) 则认为与圆相离,向顺时针方向二分,否则认为与圆相交或相切,向逆时针方向二分。从右逼近则相反。

代码中寻找射线 \(a\) 只在第二象限内(原因在接下来会提到),因为圆心在 \(x\) 轴上方导致就算二分不到与切线很相近的位置,我们也可以找到不同于 \(d_0\) 和 \(0\) 的其它合法射线(想一想,为什么?Hint:\(10^6>10^5\))。

Step 3:枚举 \(r\) 并根据 \(d_0,d_a\) 求出 \((x,y)\)

根据圆与 \(a\) 的距离 \(d_a\) 以及 \(d_0\) 仍不足以确定这个圆:两个方程,三个变量。如果再加上 \(d_b\) 那么算起来太麻烦,精度爆炸怎么办?没有关系,注意到 \(r\) 是整数并且 \(\leq 10^5\),我们直接枚举 \(r\) 并根据 \(d_0,d_a\) 算出 \(x,y\),再检查当前的圆 \((x,y,r)\) 与 \(b\) 的距离是否等于 \(d_b\)。若相等则说明 \((x,y,r)\) 即为答案。这样精度就很优秀了。

怎么算 \(x,y\):注意到射线 \(a\) 在第二象限内,设其与 \(x\) 轴负半轴的夹角为 \(\alpha\),先将坐标轴逆时针旋转 \(\alpha\) 使得 \(a\) 落在 \(x\) 轴负半轴上,我们设原来的 \(x,y\) 经过这样的变换后新的坐标为 \(x_T,y_T\)。我们很容易求出 \(x_T,y_T\):\(y_T\) 就是 \(r+d_a\),而 \(d_0\) 在这样的变换下是不会变的(旋转变换不改变两点之间的距离),因此 \(x_T^2+y_T^2=(r+d_0)^2\)。解得 \(|x_T|\) 后还需要确定符号:究竟是在新坐标系的第一象限还是第二象限。这个可以通过求得 \(a\) 后询问交互库 \((-x_a,-y_a)\) 得到 \(d_a'\),若 \(d_a<d_a'\) 说明 \(T\) 在第二象限,令 \(x_T\gets -|x_T|\),否则 \(T\) 在第一象限,\(x_T\gets |x_T|\)。

注意到我们将坐标轴逆时针旋转了 \(\alpha\) 角度,因此还要顺时针转回来。将其乘以

\[\begin{bmatrix}\cos (-\alpha)&-\sin(-\alpha)\\\sin (-\alpha)&\cos(-\alpha)\end{bmatrix}
\]

\[\begin{bmatrix}\cos \alpha&\sin\alpha\\-\sin \alpha&\cos\alpha\end{bmatrix}
\]

即可。根据 \(\alpha\in\left[0,\dfrac \pi 2\right]\) 进行了化简。至于怎么算圆到射线的距离,点积搞搞就好了吧,实在不行可以像我一样上网现学。

至此,本题被我们完美解决。时间复杂度 \(\mathcal{O}(r)\),询问次数为 \(2\log V\),其中 \(V\) 是询问最大范围。代码中使用 \(V=2\times 10^5\),一个测试点大约询问 \(50\) 次。

const ld eps=1e-5;
const ll N=2e5; ld max(ld x,ld y){return x>y?x:y;}
bool Eq(ld x,ld y){return fabs(x-y)<eps;}
bool Isz(ld x){return fabs(x)<eps;} ld up,down,left,right;
ld dis(ld x1,ld y1,ld x2,ld y2){
return sqrt((x1-x2)*(x1-x2)+((y1-y2)*(y1-y2)));
}
ld dis(ld x1,ld y1,ld x2,ld y2,ld r){
ld dot=x1*x2+y1*y2,d=dis(0,0,x2,y2);
if(dot<eps)return max((ld)0,d-r);
ld need=dot/dis(0,0,x1,y1);
return max((ld)0,sqrt(d*d-need*need)-r);
} int swp,type;
void ref(int &x,int &y){int t=y; y=x,x=-t;}
ld query(int x,int y){
for(int j=1;j<=swp;j++)ref(x,y);
std::cout<<"? "<<x<<" "<<y<<std::endl;
ld res; std::cin>>res;
return res;
}
void answ(int x,int y,int r){
for(int j=1;j<=swp;j++)ref(x,y);
std::cout<<"! "<<x<<" "<<y<<" "<<r<<std::endl,exit(0);
}
void answer(ld x,ld y,ld r){
int xx=x<0?x-eps:x+eps;
int yy=y<0?y-eps:y+eps;
return answ(xx,yy,r+eps);
} int main(){
up=query(0,10),down=query(0,-10);
left=query(-10,0),right=query(10,0);
type=Isz(up)+Isz(down)+Isz(left)+Isz(right);
if(type==0){
ld mx=max(max(up,down),max(left,right));
if(down==mx&&right==mx)swp=1;
if(right==mx&&up==mx)swp=2;
if(up==mx&&left==mx)swp=3;
}
if(type==1){
if(Isz(left))swp=1;
if(Isz(down))swp=2;
if(Isz(right))swp=3;
}
if(type==2){
if(Isz(left)&&Isz(up))swp=1;
if(Isz(down)&&Isz(left))swp=2;
if(Isz(right)&&Isz(down))swp=3;
}
up=query(0,10),down=query(0,-10);
left=query(-10,0),right=query(10,0);
ll l=1,r=N*2-1,xx,yy; ld res;
while(l<r){
ll m=l+r>>1;
xx=-std::min(N,m),yy=N-max(0ll,m-N);
(res=query(xx,yy))>1e-6?r=m:l=m+1;
}
xx=-std::min(N,l),yy=N-max(0ll,l-N);
ld upl=query(xx,yy),downr=query(-xx,-yy);
ld mdis=std::min(upl,downr); ll xx2,yy2; l=1,r=N*3-1;
while(l<r){
ll m=(l+r>>1)+1;
xx2=std::min(N,m),yy2=-N+max(0ll,m-N);
(res=query(xx2,yy2)>1e-6)?l=m:r=m-1;
}
xx2=std::min(N,l),yy2=-N+max(0ll,l-N);
res=query(xx2,yy2);
for(int r=1;r<=1e5;r++){
ld y=mdis+r,x=sqrt((down+r)*(down+r)-y*y);
ld sn=(ld)fabs(yy)/(ld)(sqrt(xx*xx+yy*yy));
ld cs=(ld)fabs(xx)/(ld)(sqrt(xx*xx+yy*yy));
if(Eq(mdis,upl))x=-x;
ld x0=x*cs+y*sn,y0=-x*sn+y*cs; x=x0,y=y0;
if(Eq(res,dis(xx2,yy2,x,y,r)))answer(x,y,r);
}
return 0;
}
// qwq Kelly qwq

CF1578I Interactive Rays:ICPC WF Moscow Invitational Contest I 题解的更多相关文章

  1. 2018 ICPC Pacific Northwest Regional Contest I-Inversions 题解

    题目链接: 2018 ICPC Pacific Northwest Regional Contest - I-Inversions 题意 给出一个长度为\(n\)的序列,其中的数字介于0-k之间,为0 ...

  2. ACM ICPC 2015 Moscow Subregional Russia, Moscow, Dolgoprudny, October, 18, 2015 G. Garden Gathering

    Problem G. Garden Gathering Input file: standard input Output file: standard output Time limit: 3 se ...

  3. ACM ICPC 2015 Moscow Subregional Russia, Moscow, Dolgoprudny, October, 18, 2015 D. Delay Time

    Problem D. Delay Time Input file: standard input Output file: standard output Time limit: 1 second M ...

  4. 2018-2019 ICPC, NEERC, Southern Subregional Contest

    目录 2018-2019 ICPC, NEERC, Southern Subregional Contest (Codeforces 1070) A.Find a Number(BFS) C.Clou ...

  5. ACM ICPC Central Europe Regional Contest 2013 Jagiellonian University Kraków

    ACM ICPC Central Europe Regional Contest 2013 Jagiellonian University Kraków Problem A: Rubik’s Rect ...

  6. Codeforces 2018-2019 ICPC, NEERC, Southern Subregional Contest

    2018-2019 ICPC, NEERC, Southern Subregional Contest 闲谈: 被操哥和男神带飞的一场ACM,第一把做了这么多题,荣幸成为7题队,虽然比赛的时候频频出锅 ...

  7. 2016 NEERC, Moscow Subregional Contest K. Knights of the Old Republic(Kruskal思想)

    2016 NEERC, Moscow Subregional Contest K. Knights of the Old Republic 题意:有一张图,第i个点被占领需要ai个兵,而每个兵传送至该 ...

  8. 2019-2020 ICPC, Asia Jakarta Regional Contest (Online Mirror, ICPC Rules, Teams Preferred)

    2019-2020 ICPC, Asia Jakarta Regional Contest (Online Mirror, ICPC Rules, Teams Preferred) easy: ACE ...

  9. ICPC North Central NA Contest 2018

    目录 ICPC North Central NA Contest 2018 1. 题目分析 2. 题解 A.Pokegene B.Maximum Subarrays C.Rational Ratio ...

随机推荐

  1. Python语法2

    目录 选择结构 循环结构 文件读写 函数 类和对象 选择结构 if,elif,else,使用时注意条件的先后顺序 通过缩进四个空格来区分代码块 # 从控制台输入 age = int(input(&qu ...

  2. MySQL:基础语法-2

    MySQL:基础语法-2 记录一下 MySQL 基础的一些语法,便于查询,该部分内容主要是参考:bilibili 上 黑马程序员 的课程而做的笔记,由于时间有点久了,课程地址忘记了 上文MySQL:基 ...

  3. 请问为什么要用三极管驱动mos,直接用mos有什么缺点呢?

    可能无法完全导通,电流可能过小使导通所需时间变长,最终导致发热严重       回复 举报     csaaa DIY七级 3# 发表于 2016-7-12 14:11:59 直接驱动mos也没什么问 ...

  4. 查找最小生成树:克鲁斯克尔算法(Kruskal)算法

    一.算法介绍 Kruskal算法是一种用来查找最小生成树的算法,由Joseph Kruskal在1956年发表.用来解决同样问题的还有Prim算法和Boruvka算法等.三种算法都是贪心算法的应用.和 ...

  5. JS基础面试

    1. JS是高级语言弱类型语言 脚本语言 1.1高级语言我们写完的代码不能直接执行,要先经过js引擎翻译成0101这种机器语言才能执行 1.2 弱类型语言变量可以在前一行设置为一个数字,下一行修改为一 ...

  6. oeasy教您玩转vim - 57 - # 行可视化

    ​ 可视化编辑 回忆上节课内容 上次我们了解到可视模式 其实可视化对应三种子模式 字符可视模式 v 行可视模式 大写V 块可视模式ctrl+v 我们先来了解字符可视化模式 快捷键 v 可配合各种mot ...

  7. 【mysql2】下载安装mysql5.7版|不再更新系列

    一.下载MySQL 5.7 版 MySQL 5.7 版:官网下载地址 https://dev.mysql.com/downloads/windows/installer/5.7.html 下载的是50 ...

  8. Redis网络库源码分析(2)之启动服务器

    一.从main开始 main函数定义在server.c中,它的内容如下: //server.c int main() { signal(SIGPIPE, SIG_IGN); //忽略SIGPIPE信号 ...

  9. MySQL到底能否解决幻读问题

    先说结论,MySQL 存储引擎 InnoDB 在可重复读(RR)隔离级别下是解决了幻读问题的. 方法:是通过next-key lock在当前读事务开启时,1.给涉及到的行加写锁(行锁)防止写操作:2. ...

  10. C++ 函数模板实现原理剖析

    C++ 函数模板实现机制原理剖析 重点 编译器并不是把函数模板处理成能够处理任意类的函数 编译器从函数模板通过具体类型来产生不同的函数 编译器会对函数模板进行两次编译 (1)在声明的位置对模板代码进行 ...