本文将同步发布于:

题目

题目链接:洛谷 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. 网络原理常用CMD命令字

    1. 运行"ipconfig/all"命令,将命令运行结果截图贴上来,并指出本机IP地址和网关IP 2. 运行"ping"命令,目标参数分别是 邻居IP.com ...

  2. 【近取 Key】Alpha - v1.0 版本发布说明

    功能与特性 Alpha 版本虽然为本软件的第一代版本,但已基本覆盖了用户个人使用时的主要功能.除登陆注册与后台管理外,下文将分版块详细介绍面向用户的主要功能特性. 『产品主页』 潜在应用场景 场景 0 ...

  3. [bug] MapReduce卡死

    参考 https://blog.csdn.net/WYpersist/article/details/80202055

  4. [Python] tkinter 之 Listbox & Combobox

    示例: 1 #用户界面 2 import os 3 os.chdir('F:\\spyder_workspace\\ColCal') 4 import Main 5 from tkinter impo ...

  5. 转圈 箭头 ⟳ 10227 27F3 刷新 HTML常用的特殊符号总结

    HTML常用的特殊符号总结 2014年9月12日 57621次浏览 html中经常会用到一些特殊符号,例如箭头,雪花,心形等等,这些符号就不用css样式或者图片来写了,直接用html特殊符号可以实现. ...

  6. CPU 使用率 100% 怎么办

    CPU 使用率 100% 怎么办 独家号 码上实战 作者 flyhero原文链接 工作中遇到CPU使用率100%,不要慌,一起来找出原因并fix掉它. 记住这里大致流程,当线上突然遇到时,也不必手足无 ...

  7. 016.Python闭包函数以及locals和globals

    一 闭包函数 内函数使用了外函数的局部变量,并且外函数把内函数返回出来的过程叫做闭包,这个内函数叫做闭包函数 1.1 闭包函数语法 def outer(): a = 5 def inner(): pr ...

  8. mysql基础之mariadb库管理和表管理语句

    一.数据库管理语句 1.Syntax: CREATE {DATABASE | SCHEMA} [IF NOT EXISTS] db_name [create_specification] ... cr ...

  9. MyBatis 动态 SQL 语句中出现 '<' 的问题

    问题描述 映射接口方法如下: /** * 根据姓名和年龄查询用户信息 * @param name 姓名 * @param user 获取年龄 * @return */ public List<U ...

  10. mybatis 同时使用 XML 和注解

    引言 学过 MyBatis 框架的童靴都知道, MyBatis 支持 XML 和注解两种方式配置,如下: 使用 XML 方式配置 只写明 XML 的 resource 路径(或者URL路径) < ...