「题解」POI2005 AKC-Special Forces Manoeuvres
本文将同步发布于:
题目
题意简述
给定 \(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|\)。
那么我们有:
\]
那么我们有:
\]
然后考虑 \(\triangle AIB\sim\triangle CHG\),我们有:
\]
我们可由此解出坐标,其他同理可算出。
#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的更多相关文章
- 「题解」「HNOI2013」切糕
文章目录 「题解」「HNOI2013」切糕 题目描述 思路分析及代码 题目分析 题解及代码 「题解」「HNOI2013」切糕 题目描述 点这里 思路分析及代码 题目分析 这道题的题目可以说得上是史上最 ...
- 「题解」「美团 CodeM 资格赛」跳格子
目录 「题解」「美团 CodeM 资格赛」跳格子 题目描述 考场思路 思路分析及正解代码 「题解」「美团 CodeM 资格赛」跳格子 今天真的考自闭了... \(T1\) 花了 \(2h\) 都没有搞 ...
- 「题解」JOIOI 王国
「题解」JOIOI 王国 题目描述 考场思考 正解 题目描述 点这里 考场思考 因为时间不太够了,直接一上来就着手暴力.但是本人太菜,居然暴力爆 000 ,然后当场自闭- 一气之下,发现对 60pts ...
- 「题解」:[loj2763][JOI2013]现代豪宅
问题 A: 现代豪宅 时间限制: 1 Sec 内存限制: 256 MB 题面 题目描述 (题目译自 $JOI 2013 Final T3$「現代的な屋敷」) 你在某个很大的豪宅里迷路了.这个豪宅由东 ...
- 「题解」:$Six$
问题 A: Six 时间限制: 1 Sec 内存限制: 512 MB 题面 题面谢绝公开. 题解 来写一篇正经的题解. 每一个数对于答案的贡献与数本身无关,只与它包含了哪几个质因数有关. 所以考虑二 ...
- 「题解」:$Smooth$
问题 A: Smooth 时间限制: 1 Sec 内存限制: 512 MB 题面 题面谢绝公开. 题解 维护一个队列,开15个指针,对应前15个素数. 对于每一次添加数字,暴扫15个指针,将指针对应 ...
- 「题解」:Kill
问题 A: Kill 时间限制: 1 Sec 内存限制: 256 MB 题面 题面谢绝公开. 题解 80%算法 赛时并没有想到正解,而是选择了另一种正确性较对的贪心验证. 对于每一个怪,我们定义它的 ...
- 「题解」:y
问题 B: y 时间限制: 1 Sec 内存限制: 256 MB 题面 题面谢绝公开. 题解 考虑双向搜索. 定义$cal_{i,j,k}$表示当前已经搜索状态中是否存在长度为i,终点为j,搜索过边 ...
- 「题解」:x
问题 A: x 时间限制: 1 Sec 内存限制: 256 MB 题面 题面谢绝公开. 题解 赛时想到了正解并且对拍了很久.对拍没挂,但是评测姬表示我w0了……一脸懵逼. 不难证明,如果对于两个数字 ...
随机推荐
- 简述MySQL优化
数据库的优化可以从四个方面来优化: 1.结构层: web服务器采用负载均衡服务器,mysql服务器采用主从复制,读写分离 2.储存层: 采用合适的存储引擎,采用三范式 3.设计层: 采用分区分表,索引 ...
- 【Azure 环境】基于Azure搭建企业级内部站点, 配置私有域名访问的详细教程 (含演示动画)
前言 在Azure中,可以通过App Service快速部署,构建自定义站点(PaaS服务).默认情况下,这些站点被访问URL都是面向公网,通过公网进行解析.为了最好的安全保障,是否可以有一种功能实现 ...
- SQL中那么多函数,Java8为什么还要提供重复的Stream方法,多此一举?
有个同学提出一个这样的疑问; 在业务系统中,数据一般都从sql中查询,类似使用where,order by,limit,聚合函数等,为什么还要用java8的Stream方法? 对这个问题,大家有什么见 ...
- 深入源码,深度解析Java 线程池的实现原理
java 系统的运行归根到底是程序的运行,程序的运行归根到底是代码的执行,代码的执行归根到底是虚拟机的执行,虚拟机的执行其实就是操作系统的线程在执行,并且会占用一定的系统资源,如CPU.内存.磁盘.网 ...
- Docker Swarm(十)Portainer 集群可视化管理
前言 搭建好我们的容器编排集群,那我们总不能日常的时候也在命令行进行操作,所以我们需要使用到一些可视化的工具,Docker图形化管理提供了很多工具,有Portainer.Docker UI.Shipy ...
- 1.5 RPM红帽软件包1.6 Yum软件仓库
1.5 RPM红帽软件包 在RPM(红帽软件包管理器)公布之前,要想在Linux系统中安装软件只能采取源码包的方式安装.早期在Linux系统中安装程序是一件非常困难.耗费耐心的事情,而且大多数的服务程 ...
- 云计算OpenStack---虚拟机获取不到ip(12)
一.现象描述 openstack平台中创建虚拟机后,虚拟机在web页面中显示获取到了ip,但是打开虚拟机控制台后查看网络状态,虚拟机没有ip地址,下图为故障截图: 二.分析 1.查看neutron服务 ...
- python基础之错误、调试(异常处理)
在程序运行过程中,总会遇到各种各样的错误. 有的错误是程序编写有问题造成的,比如本来应该输出整数结果输出了字符串,这种错误我们通常称之为bug,bug是必须修复的. 有的错误是用户输入造成的,比如让用 ...
- python 中 list 的append与extend区别
append 添加的是一个对象 extend 添加的是序列与原序列合并
- https 真的安全吗,可以抓包吗,如何防止抓包吗
Android_interview github 地址 大家好,我是程序员徐公,加上实习,有五年中大厂经验.自荐一下,可以关注我的微信公众号程序员徐公 公众号程序员徐公回复黑马,获取 Android ...