传送门


如果我们对于每一个点能找到与其相邻的点(即不经过其他点监视范围能够直接到达其监视范围的点)和是否直接到达边界,就可以直接BFS求最短路求出答案。

所以当前最重要的问题是如何找到对于每一个点相邻的点。

如果你知道泰森多边形,你就可以发现所有点的监视范围刚好对应这些点在这个矩形里的泰森多边形。

因为两个点监视范围的分界线一定在这两个点对应线段的中垂线上,所以将当前点到所有点的中垂线拿出来跑一遍半平面交,如果某个点与当前点的中垂线在半平面交中,那么这两个点就相邻。

还需要知道对于某个点能否不经过其他点的监视范围到达边界,这只需要在求半平面交的时候将矩形的四边也加上就可以了。

时间复杂度\(O(TN^2logN)\),可能需要轻微的常数优化。

写计算几何的时候务必注意细节,否则可能因为把\(xy\)坐标打反等小错误调很久QAQ

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
//This code is written by Itst
using namespace std; #define ld long double
const ld eps = 1e-12;
bool cmp(ld a , ld b){return a - eps < b && a + eps > b;} struct comp{
ld x , y , dir;
comp(ld _x = 0 , ld _y = 0) : x(_x) , y(_y){dir = atan2(y , x);}
comp operator +(comp a){return comp(x + a.x , y + a.y);}
comp operator -(comp a){return comp(x - a.x , y - a.y);}
comp operator *(ld a){return comp(x * a , y * a);}
ld operator *(comp a){return x * a.x + y * a.y;}
ld operator %(comp a){return x * a.y - y * a.x;}
bool operator <(const comp a)const{return dir < a.dir;}
bool operator ==(const comp a)const{return cmp(dir , a.dir);}
}now[607]; struct line{
comp pos , dir;
int ind;
line(comp a = comp(0,0) , comp b = comp(0,0) , int id = 0) : pos(a) , dir(b) , ind(id){}
bool operator <(const line a)const{return dir < a.dir || dir == a.dir && ((comp)a.pos - pos) % dir > 0;}
}cur[607]; struct Edge{
int end , upEd;
}Ed[370007];
int head[607] , que[607];
int cntEd , N , X0 , Y0 , X1 , Y1 , hd , tl , T;
bool mrk[607] , vis[607]; inline void addEd(int a , int b){
Ed[++cntEd] = (Edge){b , head[a]};
head[a] = cntEd;
} comp rot(comp a){
ld Cos = 0 , Sin = 1;
return comp(a.x * Cos - a.y * Sin , a.x * Sin + a.y * Cos);
} comp intersect(line a , line b){
ld t = (b.dir % (a.pos - b.pos)) / (a.dir % b.dir);
return a.pos + a.dir * t;
} bool chk(line a , line b , line c){
return (intersect(a , b) - c.pos) % c.dir > 0;
} void create(int x){
int cnt = 0;
cur[++cnt] = line(comp(0 , 0) , comp(1 , 0));
cur[++cnt] = line(comp(X1 , 0) , comp(0 , 1));
cur[++cnt] = line(comp(X1 , Y1) , comp(-1 , 0));
cur[++cnt] = line(comp(0 , Y1) , comp(0 , -1));
for(int i = 1 ; i <= N ; ++i)
if(i != x)
cur[++cnt] = line((now[i] + now[x]) * 0.5 , rot(now[i] - now[x]) , i);
sort(cur + 1 , cur + cnt + 1);
hd = tl = que[1] = 1;
for(int i = 2 ; i <= cnt ; ++i){
if(cur[i].dir == cur[i - 1].dir) continue;
while(hd < tl && chk(cur[que[tl]] , cur[que[tl - 1]] , cur[i]))
--tl;
while(hd < tl && chk(cur[que[hd]] , cur[que[hd + 1]] , cur[i]))
++hd;
que[++tl] = i;
}
while(hd < tl && chk(cur[que[tl]] , cur[que[tl - 1]] , cur[que[hd]]))
--tl;
while(hd <= tl){
if(!cur[que[hd]].ind) mrk[x] = 1;
else addEd(cur[que[hd]].ind , x);
++hd;
}
} int main(){
#ifndef ONLINE_JUDGE
freopen("in","r",stdin);
//freopen("out","w",stdout);
#endif
ios::sync_with_stdio(0);
for(cin >> T ; T ; --T){
cntEd = 0;
memset(mrk , 0 , sizeof(mrk));
memset(vis , 0 , sizeof(vis));
memset(head , 0 , sizeof(head));
cin >> N >> X1 >> Y1 >> X0 >> Y0;
if(!N){cout << "0\n"; continue;}
for(int i = 1 ; i <= N ; ++i)
cin >> now[i].x >> now[i].y;
for(int i = 1 ; i <= N ; ++i)
create(i);
queue < int > q;
ld minDis = 1e18;
int minInd = 0;
for(int i = 1 ; i <= N ; ++i){
ld dis = sqrt((now[i].x - X0) * (now[i].x - X0) + (now[i].y - Y0) * (now[i].y - Y0));
if(dis < minDis){minDis = dis; minInd = i;}
}
vis[minInd] = 1;
q.push(minInd);
bool f = 0;
for(int i = 1 ; !f ; ++i){
for(int j = q.size() ; !f && j ; --j){
int t = q.front(); q.pop();
if(mrk[t]){f = 1; continue;}
for(int i = head[t] ; i ; i = Ed[i].upEd)
if(!vis[Ed[i].end]){
vis[Ed[i].end] = 1;
q.push(Ed[i].end);
}
}
if(f) cout << i << endl;
}
}
return 0;
}

BZOJ3199 SDOI2013 逃考 半平面交、最短路的更多相关文章

  1. [BZOJ3199][SDOI2013]escape:半平面交

    分析 好像叫V图什么的. 容易发现,对于每个点,其监视的范围就是这个点与其它所有点的垂直平分线分割平面后的半平面交.由于数据范围很小,所以我们可以直接枚举每个点,使用双端队列求出其监视的范围.若两个点 ...

  2. Luogu3297 SDOI2013逃考(半平面交+最短路)

    把每个人的监视范围看成点,相邻的两个监视范围连边,那么跑一遍最短路就可以了(事实上边权都为1可以直接bfs).显然存在最优路线没有某个时刻同时被多于两人监视,要到达另一个区域的话完全可以经过分界线而不 ...

  3. 洛谷 P3297 [SDOI2013]逃考 解题报告

    P3297 [SDOI2013]逃考 题意 给一个平面矩形,里面有一些有标号点,有一个是人物点,人物点会被最近的其他点控制,人物点要走出矩形,求人物点最少被几个点控制过. 保证一开始只被一个点控制,没 ...

  4. 【BZOJ-4515】游戏 李超线段树 + 树链剖分 + 半平面交

    4515: [Sdoi2016]游戏 Time Limit: 40 Sec  Memory Limit: 256 MBSubmit: 304  Solved: 129[Submit][Status][ ...

  5. poj3335 半平面交

    题意:给出一多边形.判断多边形是否存在一点,使得多边形边界上的所有点都能看见该点. sol:在纸上随手画画就可以找出规律:按逆时针顺序连接所有点.然后找出这些line的半平面交. 题中给出的点已经按顺 ...

  6. POJ3525 半平面交

    题意:求某凸多边形内部离边界最远的点到边界的距离 首先介绍半平面.半平面交的概念: 半平面:对于一条有向直线,它的方向的左手侧就是它所划定的半平面范围.如图所示: 半平面交:多个半平面的交集.有点类似 ...

  7. POJ 3130 How I Mathematician Wonder What You Are! /POJ 3335 Rotating Scoreboard 初涉半平面交

    题意:逆时针给出N个点,求这个多边形是否有核. 思路:半平面交求多边形是否有核.模板题. 定义: 多边形核:多边形的核可以只是一个点,一条直线,但大多数情况下是一个区域(如果是一个区域则必为 ).核内 ...

  8. bzoj2618[Cqoi2006]凸多边形 半平面交

    这是一道半平面交的裸题,第一次写半平面交,就说一说我对半平面交的理解吧. 所谓半平面交,就是求一大堆二元一次不等式的交集,而每个二元一次不等式的解集都可以看成是在一条直线的上方或下方,联系直线的标准方 ...

  9. POJ 3384 Feng Shui 半平面交

    题目大意:一个人很信"Feng Shui",他要在房间里放两个圆形的地毯. 这两个地毯之间可以重叠,可是不能折叠,也不能伸到房间的外面.求这两个地毯可以覆盖的最大范围.并输出这两个 ...

随机推荐

  1. Android项目实战(四十七):轮播图效果Viewpager

    简易.常用的轮播图效果ViewPager ,老技术了,记一笔留着以后ctrl C + ctrl V    需求如下: 不定张个数的ImagView轮播,右下角显示轮播点图标,每隔固定时间切换下一张,最 ...

  2. "BLAME" is out.

    The latest feature animation film "BLAME" is watchable on the Netflix. Rendering was done ...

  3. 腾讯云 Game-Tech 技术沙龙小游戏专场“空降”长沙

    欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由腾讯游戏云发表于云+社区专栏 小游戏作为今年快速成长的新生态,在开放进入市场之后持续成为行业热点,获得了游戏开发商的高度关注与参与.在 ...

  4. Jenkins 安装 on centos7

    本文演示如何在CentOS7上安装jenkins. 1 准备工作 1.1 选择安装节点 因为在DevOps实践环境搭建规划中,Jenkins的任务需要执行docker swarm的相关命令,简单起见, ...

  5. katalon之web文件上传

    参考:https://docs.katalon.com/katalon-studio/docs/webui-upload-file.html#example- 适用范围:tag=input, type ...

  6. web前端(15)—— JavaScript的数据类型,语法规范2

    Object对象 说这个对象之前,如果您对编程语言开发稍微有点了解的话,应该知道面向对象是什么意思,而js也有面向对象一说,就因为如此,js才会这么强大. 什么是面向对象 其实所有支持面向对象的编程语 ...

  7. 个人对于 Maven 的理解

    个人对于 Maven 的理解 Maven 一直都在使用, 但如果说是不是真的懂 Maven, 很难谈得上. 或许什么时候系统地学习一下, 但在那之前, 打算先记下自己目前对于 Maven 的理解, 之 ...

  8. python集合与字典的用法

    python集合与字典的用法 集合: 1.增加  add 2.删除   •del 删除集合 •discard(常用)删除集合中的元素  #删除一个不存在的元素不会报错 •remove 删除一个不存在的 ...

  9. Win10 C盘桌面文件右上方的两个蓝色箭头解决方案

    之前看网上有很多桌面蓝色箭头的解决方案,也进行了一些尝试 可是每次Win10系统更新之后蓝色箭头就会重新显示. 最终方案:将建立在桌面的C盘文件移到D盘,桌面创建对应的快捷方式. 一劳永逸,暴力破解.

  10. PE 添加系统管理员账号(域控可加)转

    使用U盘制作一个PE系统,这里推荐老毛桃或者大白菜:开机进入Bios,选择U盘启动:进入U盘启动画面后,选择一个PE系统:进入PE系统后,我们去本机系统盘,将 C:/Windows/System32/ ...