题目

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

题目大意

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

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

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


思考历程

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

可问题是,怎么求啊?!

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


正解

正解是求半平面交……

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

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

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

先把这些打一遍。

然后我们求出每条直线的解析式(当然是向量表示),按照极角排序(就是直线和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. hexo next主题深度优化(三),引入require.js,适配pjax。

    文章目录 require.js的好处, hexo next中加入require.js 新建一个main.js作为所有js的入口 pjax的require.js实现 关于require js适配过程中报 ...

  2. LeetCode刷题笔记-贪心法-格雷编码

    题目描述: 格雷编码是一个二进制数字系统,在该系统中,两个连续的数值仅有一个位数的差异. 给定一个代表编码总位数的非负整数 n,打印其格雷编码序列.格雷编码序列必须以 0 开头. 来源:力扣(Leet ...

  3. 零基础入门学习python--第一章

    知识点汇总1. Python的应用范围:操作系统.3D动画.WEB.企业应用.云计算等.2. Python是什么类型的语言?脚本语言,即电脑编程语言,比C.C++或java之类的系统编程语言简单容易. ...

  4. C#& Screen 类&(&多&屏&幕&开&发)

    原文:C#& Screen 类&(&多&屏&幕&开&发) Screen 类 下面的代码示例演示如何使用 Screen 类的各种方法和属性. 该示 ...

  5. 【学术篇】SDOI2009 最优图像

    又是一道辣鸡卡常数题…. luogu上有些题的时限还是有毒的… 最后也只能靠O2过掉了… 不过给我原题当时的2s我随便过给你看嘛, 哪怕评测姬慢50%都没关系的.. 贴一下codevs的截图… 你看最 ...

  6. c_数据结构_哈希表

    #include <stdio.h> #include <stdlib.h> #include <string.h> #define ERROR 0 #define ...

  7. 查看收到的邮件的来源ip以及包信息

    有时我们需要知道收到的邮件是从哪台服务器发送过来的,或者想知道该邮件的报文头是怎样的.以下以网易邮箱为例介绍如果抓取这些信息. 首先我们需要知道网易邮箱的访问服务器(拉协议),由于SMTP是推的协议, ...

  8. css---flex布局--容器

    http://www.ruanyifeng.com/blog/2015/07/flex-grammar.html?utm_source=tuicool 容器设置 新版的为display为flex    ...

  9. mysql双主热备

    先搭建mysql主从模式,主从请参考mysql 主从笔记 然后在在配置文件里添加如下配置 1 log_slave_updates= #双主热备的关键参数.默认情况下从节点从主节点中同步过来的修改事件是 ...

  10. mysql 的特殊函数

    1. FORMAT() 函数 函数用于对字段的显示进行格式化 语法: SELECT FORMAT(column_name,format) FROM table_name column_name  必需 ...