本文将同步发布于:

题目

题目链接:洛谷 P3428官网

题意简述

给定 \(n\) 个圆 \((x_i,y_i,r_i)\),每个圆对应一个点集 \(S_i=\left\{(x,y)\mid (x-x_i)^2+(y-y_i)^2\leq r_i^2\right\}\)。

求一个最小的 \(i\) 满足 \(\cap_{j=1}^i S_j=\varnothing\);如果无解输出 NIE

题解

简单又自然的随机化

我们考虑枚举 \(i\),然后判定 \(S_{1\sim i}\) 的交集是否为空。

如何判定呢?我们想到一个简单的方法,我们随机一些在圆的边界上的点,只需要判定这些点是否存至少在一个点在所有圆内即可。

这种方法简单又自然,但是随机化算法正确率不高,这远远不够。

研究几何性质

如果做计算几何题而抛弃几何性质,所得到的做法往往是劣解。

继续沿着上面的思路,我们同样考虑枚举 \(i\),然后判定 \(S_{1\sim i}\) 的交集是否为空。

不同的是,我们定义一个交集中横坐标最大的点为代表点(代表点只会有一个,这是因为圆是凸集,凸集的交集还是凸集)。

我们发现,如果一些圆的交集非空,那么其代表点一定满足:它是所有圆两两交集的代表中横坐标最小的那个。

证明十分显然,考虑交集的意义即可。

最后的结论

综上所述,对于一个 \(i\),我们只需要求出 \(1\sim i-1\) 与 \(i\) 的代表点即可,如果所有代表点中横坐标最小的那一个在所有的圆内,那么其合法,否则不合法,换言之,答案为 \(i\)。

我们考虑证明这个结论:

  • 若没有交集,则这个点必然不合法,符合我们的预期;
  • 若有交集,则我们需要证明这个点是交集的代表点。
    • 假设其不是交集的代表点,则交集的代表点可能在其左右;
    • 左边:不可能,若交集存在,则代表点的横坐标 \(\geq\) 当前点横坐标。
    • 右边:不可能,考虑当前点在 \(S_a\cap S_b\) 中得到,那么所有 \(x\geq\) 当前点横坐标的点均被交集抛弃,因此代表点的横坐标 \(\leq\) 当前点横坐标。
    • 由夹逼过程可知结论正确。

这个算法的时间复杂度为 \(\Theta(n^2)\)。

参考程序

下面我们来解决两圆求交的问题。

下面介绍一下两种方法:余弦定理和相似三角形。

余弦定理

用余弦定理求解需要用到三角函数,常数大,精度差。

我们考虑下图:

对 \(\triangle{ACB}\) 运用余弦定理,得到 \(r_a^2+d^2-2dr_a\cos\alpha=r_b^2\),进而求出 \(\alpha=\arccos\left(\frac{r_a^2+d^2-r_b2}{2dr_a}\right)\)。

然后我们再求出基准角 \(\beta\),显然 \(\beta=\texttt{atan2}(y_b-y_a,x_b-x_a)\)。

因此,我们得到了 \(C,D\) 两点的对 \(A\) 的极角为 \(\beta+\alpha\),\(\beta-\alpha\)。

对于极角为 \(\theta\),极径为 \(r_a\) 的点,我们得出其对应点的坐标为 \((r_a\cos\theta,r_a\sin\theta)\)。

#include<bits/stdc++.h>
using namespace std;
#define reg register
typedef long long ll; const double eps=1e-6; inline int sgn(reg double x){
if(fabs(x)<eps)
return 0;
else
return x<0?-1:1;
} inline double sqr(reg double x){
return x*x;
} const int MAXN=2e3+5; struct Vector{
double x,y;
inline Vector(reg double x=0,reg double y=0):x(x),y(y){
return;
}
inline Vector operator+(const Vector& a)const{
return Vector(x+a.x,y+a.y);
}
inline Vector operator-(const Vector& a)const{
return Vector(x-a.x,y-a.y);
}
inline Vector operator*(const double a)const{
return Vector(x*a,y*a);
}
}; inline double dot(const Vector& a,const Vector& b){
return a.x*b.x+a.y*b.y;
} inline double cross(const Vector& a,const Vector& b){
return a.x*b.y-a.y*b.x;
} typedef Vector Point; inline double getDis2(const Point& a,const Point& b){
return dot(a-b,a-b);
} inline double getDis(const Point& a,const Point& b){
return sqrt(getDis2(a,b));
} inline bool isEmpty(const Point& a){
return a.x!=a.x||a.y!=a.y;
} struct Circle{
Point o;
double r;
inline bool contain(const Point& x)const{
return sgn(sqr(r)-getDis2(x,o))>=0;
}
inline Point getRig(void)const{
return o+Vector(r,0);
}
}; inline bool isCon(const Circle& a,const Circle& b){
return sgn(sqr(a.r-b.r)-getDis2(a.o,b.o))>=0;
} inline bool isSep(const Circle& a,const Circle& b){
return sgn(getDis2(a.o,b.o)-sqr(a.r+b.r))>0;
} inline Point getPot(const Circle &a,const Circle &b){
if(isCon(a,b))
if(sgn(b.getRig().x-a.getRig().x)>0)
return a.getRig();
else
return b.getRig();
else if(isSep(a,b))
return Point(nan(""),nan(""));
else{
if(a.contain(b.getRig()))
return b.getRig();
else if(b.contain(a.getRig()))
return a.getRig();
else{
reg double d=getDis(a.o,b.o);
reg double ang=acos(((sqr(a.r)+sqr(d))-sqr(b.r))/(2*a.r*d));
reg double delta=atan2(b.o.y-a.o.y,b.o.x-a.o.x);
reg double ang1=delta+ang,ang2=delta-ang;
Point p1=a.o+Vector(cos(ang1)*a.r,sin(ang1)*a.r);
Point p2=a.o+Vector(cos(ang2)*a.r,sin(ang2)*a.r);
Point res;
if(sgn(p2.x-p1.x)>0)
res=p2;
else
res=p1;
return res;
}
}
} int n;
Circle a[MAXN]; int main(void){
scanf("%d",&n);
Point lef(0,0);
for(reg int i=1;i<=n;++i){
static int x,y,r;
scanf("%d%d%d",&x,&y,&r);
a[i].o=Point(x,y),a[i].r=r;
if(i==2)
lef=getPot(a[1],a[2]);
else if(i>2){
for(reg int j=1;j<i&&!isEmpty(lef);++j){
Point tmp=getPot(a[i],a[j]);
if(isEmpty(tmp)||tmp.x<=lef.x)
lef=tmp;
}
for(reg int j=1;j<=i&&!isEmpty(lef);++j)
if(!a[j].contain(lef))
lef=Point(nan(""),nan(""));
}
if(isEmpty(lef)){
printf("%d\n",i);
return 0;
}
}
puts("NIE");
return 0;
}

相似三角形

如上图,我们设 \(a=|AG|\),\(b=|BG|\),\(h=|CG|\)。

那么我们有:

\[\begin{cases}r_a^2=a^2+h^2\\r_b^2=b^2+h^2\\a+b=d\end{cases}
\]

那么我们有:

\[a=\frac{r_a^2+d^2-r_b^2}{2d}
\]

然后考虑 \(\triangle AIB\sim\triangle CHG\),我们有:

\[(y_b-y_a)h=d(y_c-y_g)
\]

我们可由此解出坐标,其他同理可算出。

#include<bits/stdc++.h>
using namespace std;
#define reg register
typedef long long ll; const double eps=1e-6; inline int sgn(reg double x){
if(fabs(x)<eps)
return 0;
else
return x<0?-1:1;
} inline double sqr(reg double x){
return x*x;
} const int MAXN=2e3+5; struct Vector{
double x,y;
inline Vector(reg double x=0,reg double y=0):x(x),y(y){
return;
}
inline Vector operator+(const Vector& a)const{
return Vector(x+a.x,y+a.y);
}
inline Vector operator-(const Vector& a)const{
return Vector(x-a.x,y-a.y);
}
inline Vector operator*(const double a)const{
return Vector(x*a,y*a);
}
}; inline double dot(const Vector& a,const Vector& b){
return a.x*b.x+a.y*b.y;
} inline double cross(const Vector& a,const Vector& b){
return a.x*b.y-a.y*b.x;
} typedef Vector Point; inline double getDis2(const Point& a,const Point& b){
return dot(a-b,a-b);
} inline double getDis(const Point& a,const Point& b){
return sqrt(getDis2(a,b));
} inline bool isEmpty(const Point& a){
return isnan(a.x)||isnan(a.y);
} struct Circle{
Point o;
double r;
inline bool contain(const Point& x)const{
return sgn(sqr(r)-getDis2(x,o))>=0;
}
inline Point getRig(void)const{
return o+Vector(r,0);
}
}; inline bool isCon(const Circle& a,const Circle& b){
return sgn(sqr(a.r-b.r)-getDis2(a.o,b.o))>=0;
} inline bool isSep(const Circle& a,const Circle& b){
return sgn(getDis2(a.o,b.o)-sqr(a.r+b.r))>0;
} inline Point getPot(const Circle &a,const Circle &b){
if(isCon(a,b))
if(sgn(b.getRig().x-a.getRig().x)>0)
return a.getRig();
else
return b.getRig();
else if(isSep(a,b))
return Point(nan(""),nan(""));
else{
if(a.contain(b.getRig()))
return b.getRig();
else if(b.contain(a.getRig()))
return a.getRig();
else{
reg double d=getDis(a.o,b.o);
reg double val=(sqr(a.r)+sqr(d)-sqr(b.r))/(2*d);
reg double h=sqrt(sqr(a.r)-sqr(val));
Point bas=a.o+(b.o-a.o)*(val/d);
Vector tmp=Vector(b.o.y-a.o.y,a.o.x-b.o.x)*(h/d);
Point p1=bas-tmp,p2=bas+tmp;
if(sgn(p2.x-p1.x)>0)
return p2;
else
return p1;
}
}
} int n;
Circle a[MAXN]; int main(void){
scanf("%d",&n);
Point lef(0,0);
for(reg int i=1;i<=n;++i){
static int x,y,r;
scanf("%d%d%d",&x,&y,&r);
a[i].o=Point(x,y),a[i].r=r;
if(i==2)
lef=getPot(a[1],a[2]);
else if(i>2){
for(reg int j=1;j<i&&!isEmpty(lef);++j){
Point tmp=getPot(a[i],a[j]);
if(isEmpty(tmp)||tmp.x<=lef.x)
lef=tmp;
}
for(reg int j=1;j<=i&&!isEmpty(lef);++j)
if(!a[j].contain(lef))
lef=Point(nan(""),nan(""));
}
if(isEmpty(lef)){
printf("%d\n",i);
return 0;
}
}
puts("NIE");
return 0;
}

「题解」POI2005 AKC-Special Forces Manoeuvres的更多相关文章

  1. 「题解」「HNOI2013」切糕

    文章目录 「题解」「HNOI2013」切糕 题目描述 思路分析及代码 题目分析 题解及代码 「题解」「HNOI2013」切糕 题目描述 点这里 思路分析及代码 题目分析 这道题的题目可以说得上是史上最 ...

  2. 「题解」「美团 CodeM 资格赛」跳格子

    目录 「题解」「美团 CodeM 资格赛」跳格子 题目描述 考场思路 思路分析及正解代码 「题解」「美团 CodeM 资格赛」跳格子 今天真的考自闭了... \(T1\) 花了 \(2h\) 都没有搞 ...

  3. 「题解」JOIOI 王国

    「题解」JOIOI 王国 题目描述 考场思考 正解 题目描述 点这里 考场思考 因为时间不太够了,直接一上来就着手暴力.但是本人太菜,居然暴力爆 000 ,然后当场自闭- 一气之下,发现对 60pts ...

  4. 「题解」:[loj2763][JOI2013]现代豪宅

    问题 A: 现代豪宅 时间限制: 1 Sec  内存限制: 256 MB 题面 题目描述 (题目译自 $JOI 2013 Final T3$「現代的な屋敷」) 你在某个很大的豪宅里迷路了.这个豪宅由东 ...

  5. 「题解」:$Six$

    问题 A: Six 时间限制: 1 Sec  内存限制: 512 MB 题面 题面谢绝公开. 题解 来写一篇正经的题解. 每一个数对于答案的贡献与数本身无关,只与它包含了哪几个质因数有关. 所以考虑二 ...

  6. 「题解」:$Smooth$

    问题 A: Smooth 时间限制: 1 Sec  内存限制: 512 MB 题面 题面谢绝公开. 题解 维护一个队列,开15个指针,对应前15个素数. 对于每一次添加数字,暴扫15个指针,将指针对应 ...

  7. 「题解」:Kill

    问题 A: Kill 时间限制: 1 Sec  内存限制: 256 MB 题面 题面谢绝公开. 题解 80%算法 赛时并没有想到正解,而是选择了另一种正确性较对的贪心验证. 对于每一个怪,我们定义它的 ...

  8. 「题解」:y

    问题 B: y 时间限制: 1 Sec  内存限制: 256 MB 题面 题面谢绝公开. 题解 考虑双向搜索. 定义$cal_{i,j,k}$表示当前已经搜索状态中是否存在长度为i,终点为j,搜索过边 ...

  9. 「题解」:x

    问题 A: x 时间限制: 1 Sec  内存限制: 256 MB 题面 题面谢绝公开. 题解 赛时想到了正解并且对拍了很久.对拍没挂,但是评测姬表示我w0了……一脸懵逼. 不难证明,如果对于两个数字 ...

随机推荐

  1. Hack The Box - Archetype

    攻略的话在靶场内都有,也有官方的攻略,我作为一个技术小白,只是想把自己的通关过程记录下来,没有网站内大佬们写得好 我们获得了一个IP: 尝试访问了一下,应该不存在web页面: 对常规端口进行一个扫描: ...

  2. zTree增加树形菜单格式

    result为json字符串 //展示树形菜单 function showMenuTree(result) { console.log("页面展示函数:"+result); //属 ...

  3. android之布局优化

    android中提供了<include />.<merge />.<ViewStub />三种优化布局. 1.<include /> <inclu ...

  4. 【js】Leetcode每日一题-二叉树的堂兄弟节点

    [js]Leetcode每日一题-二叉树的堂兄弟节点 [题目描述] 在二叉树中,根节点位于深度 0 处,每个深度为 k 的节点的子节点位于深度 k+1 处. 如果二叉树的两个节点深度相同,但 父节点不 ...

  5. 一种巧妙的使用 CSS 制作波浪效果的思路

    在之前,我介绍过几种使用纯 CSS 实现波浪效果的方式,关于它们有两篇相关的文章: 纯 CSS 实现波浪效果! 巧用 CSS 实现酷炫的充电动画 本文将会再介绍另外一种使用 CSS 实现的波浪效果,思 ...

  6. 缓存架构中的服务详解!SpringBoot中二级缓存服务的实现

    创建缓存服务 创建缓存服务接口项目 创建myshop-service-redis-api项目,该项目只负责定义接口 创建项目的pom.xml: <?xml version="1.0&q ...

  7. linux-TCP多线程的并发服务器- 以言责人甚易,以义持己实难!!!

    1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include <ti ...

  8. 最全的cURL命令使用

    cURL是什么 curl是Linux命令行工具,可以使用任何可支持的协议(如HTTP.FTP.IMAP.POP3.SCP.SFTP.SMTP.TFTP.TELNET.LDAP或FILE)在服务器之间传 ...

  9. alpine安装网络工具

    telnet:busybox-extras net-tools: net-tools tcpdump: tcpdump wget: wget dig nslookup: bind-tools curl ...

  10. welcome实现首页路由的重定向效果

    welcome实现首页路由的重定向效果 1.创建welcome组件 2.在路由中引入组件并配置子组件 3.在home.vue中添加路由占位符 4.测试