[JZOJ3297] 【SDOI2013】逃考
题目
我发现我现在连题面都懒得复制粘贴了……
题目大意
在一个矩形中有一堆点,这堆点按照以下规则将矩形瓜分成一堆块:
对于每个坐标,它属于离它最近的点的块。
一个人从某个坐标出发到矩形外面,求经过的最少的区域个数。
思考历程
显然,如果将每个点管辖的区域处理出来,和周围接壤的区域连一条边,答案就是一个最短路的事情。
可问题是,怎么求啊?!
作为计算几何白痴,我不得不弃疗……
正解
正解是求半平面交……
显然,对于每个点,枚举其它点,作两点连线的垂直平分线,围成的区域就是它管辖的范围。
所以这就是半平面交的裸题……
首先要有一堆计算几何的知识,向量的基本操作(点积、叉积)、求交点、点到直线距离……
先把这些打一遍。
然后我们求出每条直线的解析式(当然是向量表示),按照极角排序(就是直线和xxx轴的夹角),如果两条直线的极角一样就选离点更近的那一条。
接着用一个双向队列,有直线加进来时,看看队尾和队尾的前一条直线的交点是否在这条直线的外面,如果是,就将队尾踢掉。循环操作。
同时队头也要类似地操作。
在搞完之后试着用队头弹掉不合法的队尾。
于是半平面交就处理出来了……
将残留的直线的另一边的区域和自己连边,然后跑最短路。
代码
using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define N 610
#define PI 3.1415926535
int n;
double X,Y;
struct DOT{
double x,y;
} d[N],beg;
int nl;
struct Line{
DOT p,v;
} l[N];
inline DOT operator+(const DOT &a,const DOT &b)
{return {a.x+b.x,a.y+b.y};}
inline DOT operator-(const DOT &a,const DOT &b)
{return {a.x-b.x,a.y-b.y};}
inline DOT operator*(const DOT &a,const double b)
{return {a.x*b,a.y*b};}
inline double dot(const DOT &a,const DOT &b)
{return a.x*b.x+a.y*b.y;}
inline double cro(const DOT &a,const DOT &b)
{return a.x*b.y-a.y*b.x;}
inline double len(const DOT &a)
{return sqrt(dot(a,a));}
inline double len2(const DOT &a)
{return dot(a,a);}
inline bool direct(const DOT &a,const DOT &b)
{return cro(a,b)<=0;}
inline DOT intersect(const Line &a,const Line &b)
{return b.p+b.v*(cro(a.v,a.p-b.p)/cro(a.v,b.v));}
inline double distl(const Line &a,const DOT b)
{return abs(cro(b-a.p,a.v)/len(a.v));}
int with[N];
double theta[N];
double LEN2[N];
int p[N];
inline bool cmpp(int a,int b)
{return theta[a]<theta[b] || theta[a]==theta[b] && LEN2[a]<LEN2[b];}
int q[N];
struct EDGE{
int to;
EDGE *las;
} e[N*N];
int ne;
EDGE *last[N];
inline void link(int u,int v){
e[++ne]={v,last[u]};
last[u]=e+ne;
}
int dis[N];
inline void BFS(int S){
int head=0,tail=1;
q[1]=S;
memset(dis,0,sizeof dis);
dis[S]=1;
do{
int x=q[++head];
for (EDGE *ei=last[x];ei;ei=ei->las)
if (!dis[ei->to]){
dis[ei->to]=dis[x]+1;
if (ei->to==0){
printf("%d\n",dis[ei->to]-1);
return;
}
q[++tail]=ei->to;
}
}
while (head!=tail);
}
int main(){
freopen("in.txt","r",stdin);
int T;
scanf("%d",&T);
while (T--){
scanf("%d%lf%lf%lf%lf",&n,&X,&Y,&beg.x,&beg.y);
if (n==0){
printf("0\n");
continue;
}
for (int i=1;i<=n;++i)
scanf("%lf%lf",&d[i].x,&d[i].y);
ne=0;
memset(last,0,sizeof last);
for (int i=1;i<=n;++i){
l[1]={X,0,0,1};
l[2]={X,Y,-1,0};
l[3]={0,Y,0,-1};
l[4]={0,0,1,0};
with[1]=with[2]=with[3]=with[4]=0;
nl=4;
for (int j=1;j<=n;++j)
if (i!=j){
DOT mid={(d[i].x+d[j].x)/2,(d[i].y+d[j].y)/2},t=mid-d[i];
l[++nl]={mid,-t.y,t.x};
with[nl]=j;
}
for (int j=1;j<=nl;++j){
theta[j]=atan2(l[j].v.y,l[j].v.x);
if (theta[j]<0)
theta[j]+=PI*2;
LEN2[j]=distl(l[j],d[i]);
p[j]=j;
}
sort(p+1,p+nl+1,cmpp);
int tmp=nl;
nl=0;
for (int j=1;j<=tmp;++j)
if (j==1 || theta[p[j]]!=theta[p[j-1]])
p[++nl]=p[j];
int head=1,tail=0;
for (int j=1;j<=nl;++j){
while (head<tail && direct(l[p[j]].v,intersect(l[q[tail-1]],l[q[tail]])-l[p[j]].p)==1)
--tail;
while (head<tail && direct(l[p[j]].v,intersect(l[q[head]],l[q[head+1]])-l[p[j]].p)==1)
++head;
q[++tail]=p[j];
}
while (head<tail && direct(l[q[head]].v,intersect(l[q[tail-1]],l[q[tail]])-l[q[head]].p)==1)
--tail;
for (int j=head;j<=tail;++j)
link(i,with[q[j]]);
}
int mni=1;
for (int i=2;i<=n;++i)
if (len2(d[i]-beg)<len2(d[mni]-beg))
mni=i;
BFS(mni);
}
return 0;
}
总结
必须要恶补计算几何
在打计算几何之前,先把一堆模板打下来,这样会方便很多。
[JZOJ3297] 【SDOI2013】逃考的更多相关文章
- 洛谷 P3297 [SDOI2013]逃考 解题报告
P3297 [SDOI2013]逃考 题意 给一个平面矩形,里面有一些有标号点,有一个是人物点,人物点会被最近的其他点控制,人物点要走出矩形,求人物点最少被几个点控制过. 保证一开始只被一个点控制,没 ...
- BZOJ3199 SDOI2013 逃考 半平面交、最短路
传送门 如果我们对于每一个点能找到与其相邻的点(即不经过其他点监视范围能够直接到达其监视范围的点)和是否直接到达边界,就可以直接BFS求最短路求出答案. 所以当前最重要的问题是如何找到对于每一个点相邻 ...
- luogu P3297 [SDOI2013]逃考
传送门 gugugu 首先每个人管理的区域是一个多边形,并且整个矩形是被这样的多边形填满的.现在的问题是求一条经过多边形最少的路径到达边界,这个可以最短路. 现在的问题是建图,显然我们应该给相邻的多边 ...
- Luogu3297 SDOI2013逃考(半平面交+最短路)
把每个人的监视范围看成点,相邻的两个监视范围连边,那么跑一遍最短路就可以了(事实上边权都为1可以直接bfs).显然存在最优路线没有某个时刻同时被多于两人监视,要到达另一个区域的话完全可以经过分界线而不 ...
- P3297 [SDOI2013]逃考
传送门 完全看不出这思路是怎么来的-- 首先对于两个亲戚,他们监视范围的边界是他们连线的中垂线.那么对于一个亲戚来说它能监视的范围就是所有的中垂线形成的半平面交 然后如果某两个亲戚的监视范围有公共边, ...
- 【JZOJ3297】【SDOI2013】逃考(escape)
Mission 高考又来了,对于不认真读书的来讲真不是个好消息.为了小杨能在家里认真读书,他的亲戚决定驻扎在他的家里监督他学习,有爷爷奶奶.外公外婆.大舅.大嫂.阿姨-- 小杨实在是忍无可忍了,这种生 ...
- 2014秋C++第5周项目1參考-见识刚開始学习的人常见错误
课程主页在http://blog.csdn.net/sxhelijian/article/details/39152703,实践要求见http://blog.csdn.net/sxhelijian/a ...
- C++常考面试题汇总
c++面试题 一 用简洁的语言描述 c++ 在 c 语言的基础上开发的一种面向对象编程的语言: 应用广泛: 支持多种编程范式,面向对象编程,泛型编程,和过程化编程:广泛应用于系统开发,引擎开发:支持类 ...
- AWS的SysOps认证考试样题解析
刚考过了AWS的developer认证,顺手做了一下SysOps的样题.以下是题目和答案. When working with Amazon RDS, by default AWS is respon ...
随机推荐
- shell设置时间递减脚本
经常要用shell来做时间的定时任务,尤其是用sqoop脚本拉取数据的时候,那么假如当你要导入数据是残缺的时候呢,我写了一个能自定义时间并逐条递减的程序 #!/bin/bash . /etc/pr ...
- JAVA IntelliJ IDEA for mac/jdk的安装及环境配置、运行
现在配置完之后再回头看看,其实挺简单, 但我还是弄了好几个小时才配置出来, 不过好在是自己配置出来的, 每天都在慢慢进步. 安装及配置步骤如下: JAVA的IDE的话去jetbrains的官网上对应下 ...
- angularJS和requireJS和angularAMD
最近因为要用到angularJS开发项目,因为涉及到的静态资源比较多,所以想把js文件通过requireJS来按需加载,这两个框架以前都使用过,但是结合到一起还没有用过,那就试一下,看能否达到目的. ...
- Python服务端工程师就业面试指导✍✍✍
Python服务端工程师就业面试指导 整个课程都看完了,这个课程的分享可以往下看,下面有链接,之前做java开发也做了一些年头,也分享下自己看这个视频的感受,单论单个知识点课程本身没问题,大家看的时 ...
- C 二维数组与指针
http://c.biancheng.net/view/2022.html 1. 区分指针数组和数组指针 指针数组:存放指针的数组,如 int *pstr[5] = NULL; 数组中每个元素存放的是 ...
- Android studio 添加引用Module项目 与 设置Module项目的Libs的Jar在主项目里使用
前言 添加引用Module项目 设置Module项目的Libs的Jar在主项目里使用 1.在项目里添加libs包,并且加入jar 2.设置这个module项目的build.gradle depende ...
- sublime text-----查看当前文件的编码格式
1.preferences->settings,在user中添加 "show_encoding": true, "show_line_endings": ...
- 给大家介绍一下linux系统高级命令
输出重定向(mip.0834jl.com) ;覆盖文件内容 回声' 123 ' 测试;覆盖原始内容 回声' 123 ' 测试;原始存在(共存) echo 'ken2 '(www.jl0834.com) ...
- 专访阿里云MVP黄胜蓝:90 后 CTO花了6年,改变了你日常生活里的这件事
[黄胜蓝:现任武汉极意网络科技有限公司CTO.高中时期NOIP一等奖保送至武汉大学,大学期间曾指导团队获得世界数学建模大赛金奖,同时负责武汉大学学生校园门户网站的运维工作.于2013年加入武汉极意网络 ...
- h5判断是否为iphonex
js移动端页面判断是否是iphoneX 转自https://blog.csdn.net/weixin_39924326/article/details/80352929 function isIPho ...