题目

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

题目大意

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

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

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


思考历程

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

可问题是,怎么求啊?!

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


正解

正解是求半平面交……

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

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

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

先把这些打一遍。

然后我们求出每条直线的解析式(当然是向量表示),按照极角排序(就是直线和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. 接口(Interfaces)与反射(reflection) 如何利用字符串驱动不同的事件 动态地导入函数、模块

    标准库内部如何实现接口的 package main import ( "fmt" "io" "net/http" "os" ...

  2. C# sort System.InvalidOperationException: Failed to compare two elements in the ar

    System.InvalidOperationException: Failed to compare two elements in the array. ---> System.NullRe ...

  3. IAsyncResult

    using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; usin ...

  4. 转:container_of分析 研究内核的博客

    源地址:http://blog.csdn.net/tigerjibo/article/details/8299589 2012-12-15 19:23 1636人阅读 评论(2) 收藏 举报   目录 ...

  5. Unity 之事件系统

    游戏开发过程中事件是非常多的,可以通过 Messenger 事件系统来解藕,用法如下: 使用方法 例子:在按下拍照按钮后通知刷新好友面板 步骤1.添加事件字段,该字段具有唯一性 在MessangerE ...

  6. 剑指offer——11矩阵覆盖

    题目描述 我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形.请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?   题解: 使用递归或者动态规划,明显,递归没有动态规划优 ...

  7. 2014 0416 word清楚项目黑点 输入矩阵 普通继承和虚继承 函数指针实现多态 强弱类型语言

    1.word 如何清除项目黑点 选中文字区域,选择开始->样式->全部清除 2.公式编辑器输入矩阵 先输入方括号,接着选择格式->中间对齐,然后点下面红色框里的东西,组后输入数据   ...

  8. webpack3

    6月20号webpack推出了3.0版本,官方也发布了公告.根据公告介绍,webpack团队将未来版本的改动聚焦在社区提出的功能需求,同时将保持一个快速.稳定的发布节奏.本文主要依据公告内容,简单介绍 ...

  9. mysql安装总结

    #mysql安装# 安装工具:yum -y install gcc-c++ ncurses cmake make ncurses-devel # 拷贝配置文件到指定目录:rm -rf /etc/my. ...

  10. 将数据写到kafka的topic

    package test05 import java.util.Propertiesimport org.apache.kafka.clients.producer.{KafkaProducer, P ...