题目

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

题目大意

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

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

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


思考历程

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

可问题是,怎么求啊?!

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


正解

正解是求半平面交……

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

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

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

先把这些打一遍。

然后我们求出每条直线的解析式(当然是向量表示),按照极角排序(就是直线和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. AdaBoost笔记之通俗易懂原理介绍

    转自:https://blog.csdn.net/px_528/article/details/72963977 写在前面 说到Adaboost,公式与代码网上到处都有,<统计学习方法>里 ...

  2. vs使用出现的一些常见错误(持续更新)

    vs2010编译出错时怎么会执行上一次的结果_百度知道https://zhidao.baidu.com/question/193018332.html

  3. HDU-1754-I Hate It-线段树-求区间最值和单点修改

    开学新拉的题目,老题重做,思路会稍微比之前清晰,不过这也算是一点点进步了. 很多学校流行一种比较的习惯.老师们很喜欢询问,从某某到某某当中,分数最高的是多少. 这让很多学生很反感. 不管你喜不喜欢,现 ...

  4. 安装mongo

    版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/weixin_40101530/articl ...

  5. RHEL7更换yum源

    1,删除注册和订阅提示 查找subscription-manager相关组件: rpm -qa | grep subscription-manager 删除subscription-maanager相 ...

  6. 简单的spring--mvc整合

    <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.sp ...

  7. asp.net Core 使用redis(StackExchange.Redis)

    原文:asp.net Core 使用redis(StackExchange.Redis) 一.添加配置(appsettings.json) "Redis": { "Def ...

  8. iOS进阶一OC对象的本质

    OC对象的本质 平时编写的Object-C代码,底层实现其实都是C/C++代码. 所以Objective-C的面向对象都是基于C/C++的数据结构实现的,OC对象内部可以容纳不同数据类型的数据,因此可 ...

  9. SQL语句:一张表和另一张表的多重匹配查询

    1.两个表结构如下图 2.如何查询成如下图所示 3.SQL语句是: select id,name=stuff(( select ','+t2.name from a t1 join b t2 on c ...

  10. Spring Boot环境搭建。

    1.环境准备. jdk1.8 idea(如果不会激活可以看另外一篇:https://www.cnblogs.com/joeking/p/11119123.html) 2.打开idea 如果是idea的 ...