题目

我发现我现在连题面都懒得复制粘贴了……

题目大意

在一个矩形中有一堆点,这堆点按照以下规则将矩形瓜分成一堆块:

对于每个坐标,它属于离它最近的点的块。

一个人从某个坐标出发到矩形外面,求经过的最少的区域个数。


思考历程

显然,如果将每个点管辖的区域处理出来,和周围接壤的区域连一条边,答案就是一个最短路的事情。

可问题是,怎么求啊?!

作为计算几何白痴,我不得不弃疗……


正解

正解是求半平面交……

显然,对于每个点,枚举其它点,作两点连线的垂直平分线,围成的区域就是它管辖的范围。

所以这就是半平面交的裸题……

首先要有一堆计算几何的知识,向量的基本操作(点积、叉积)、求交点、点到直线距离……

先把这些打一遍。

然后我们求出每条直线的解析式(当然是向量表示),按照极角排序(就是直线和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】逃考的更多相关文章

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

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

  2. BZOJ3199 SDOI2013 逃考 半平面交、最短路

    传送门 如果我们对于每一个点能找到与其相邻的点(即不经过其他点监视范围能够直接到达其监视范围的点)和是否直接到达边界,就可以直接BFS求最短路求出答案. 所以当前最重要的问题是如何找到对于每一个点相邻 ...

  3. luogu P3297 [SDOI2013]逃考

    传送门 gugugu 首先每个人管理的区域是一个多边形,并且整个矩形是被这样的多边形填满的.现在的问题是求一条经过多边形最少的路径到达边界,这个可以最短路. 现在的问题是建图,显然我们应该给相邻的多边 ...

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

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

  5. P3297 [SDOI2013]逃考

    传送门 完全看不出这思路是怎么来的-- 首先对于两个亲戚,他们监视范围的边界是他们连线的中垂线.那么对于一个亲戚来说它能监视的范围就是所有的中垂线形成的半平面交 然后如果某两个亲戚的监视范围有公共边, ...

  6. 【JZOJ3297】【SDOI2013】逃考(escape)

    Mission 高考又来了,对于不认真读书的来讲真不是个好消息.为了小杨能在家里认真读书,他的亲戚决定驻扎在他的家里监督他学习,有爷爷奶奶.外公外婆.大舅.大嫂.阿姨-- 小杨实在是忍无可忍了,这种生 ...

  7. 2014秋C++第5周项目1參考-见识刚開始学习的人常见错误

    课程主页在http://blog.csdn.net/sxhelijian/article/details/39152703,实践要求见http://blog.csdn.net/sxhelijian/a ...

  8. C++常考面试题汇总

    c++面试题 一 用简洁的语言描述 c++ 在 c 语言的基础上开发的一种面向对象编程的语言: 应用广泛: 支持多种编程范式,面向对象编程,泛型编程,和过程化编程:广泛应用于系统开发,引擎开发:支持类 ...

  9. AWS的SysOps认证考试样题解析

    刚考过了AWS的developer认证,顺手做了一下SysOps的样题.以下是题目和答案. When working with Amazon RDS, by default AWS is respon ...

随机推荐

  1. Oracle 生成sys_guid

    select sys_guid() from dual;select sys_guid() from dual connect by rownum<100

  2. (转)Python之路,Day6 - 面向对象学习

    本节内容:   面向对象编程介绍 为什么要用面向对象进行开发? 面向对象的特性:封装.继承.多态 类.方法.     引子 你现在是一家游戏公司的开发人员,现在需要你开发一款叫做<人狗大战> ...

  3. <pygame> 打飞机(小游戏)

    0.游戏的基本实现 ''' 游戏的基本实现 游戏的初始化:设置游戏窗口,绘制图像的初始位置,设定游戏时钟 游戏循环:设置刷新频率,检测用户交互,更新所有图像位置,更新屏幕显示 ''' 1.安装pyga ...

  4. vue3+node全栈项目部署到云服务器

    一.前言 最近在B站学习了一下全栈开发,使用到的技术栈是Vue+Element+Express+MongoDB,为了让自己学的第一个全栈项目落地,于是想着把该项目部署到阿里云服务器.经过网上一番搜索和 ...

  5. 与960 Grid System相关的那些问题

    为什么是960px? 一直以来,网页设计师都希望寻找一个理想的页面宽度值,既能适应大部分屏幕,又尽可能的在一行显示更多的信息. 我们首先会考虑的是全屏自适应,但这并非一个好的解决方案.一方面,需要做一 ...

  6. mysql UDF提权问题

    测试UDF提权,时候遇到问题,创建函数shell提示存在 当执行操作的时候又提示,shell函数不存在. FUNCTION mysql.shell does not exist 如果在测试环境下,一般 ...

  7. Impala系统架构

  8. 既然 start() 方法会调用 run() 方法,为什么我们调用 start() 方法,而不直接调用 run() 方法?

    当你调用 start() 方法时,它会新建一个线程然后执行 run() 方法中的代码.如果直接调用 run() 方法,并不会创建新线程,方法中的代码会在当前调用者的线程中执行

  9. grep每次读取多大的文本

    Most of the tools do not actually read a single line in from a file at a time, rather they use a buf ...

  10. 20170702-变量说明,静态方法,类方法区别,断点调试,fork,yield协程,进程,动态添加属性等。。

    概念: 并行:同时运行 并发:看似同时运行  json后任然中文的问题 import json d = {"名字":"初恋这件小事"} new_d1 = jso ...