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

时间:2023-06-30 18:17:13

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

  现在问题是怎么求出哪些监视范围相邻。考虑对于某个人的监视范围求出所有与它相邻的。两个监视范围的公共边是这两个人连线的中垂线,把这些线画出来可以发现求个半平面交就好了。注意线要求在矩形范围内。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
int read()
{
int x=,f=;char c=getchar();
while (c<''||c>'') {if (c=='-') f=-;c=getchar();}
while (c>=''&&c<='') x=(x<<)+(x<<)+(c^),c=getchar();
return x*f;
}
#define N 610
#define vector dot
int T,n,p[N],d[N],queue[N],cnt;
bool flag[N];
const double eps=1E-;
struct data{int to,nxt;
}edge[N*N];
struct dot
{
double x,y;
vector operator +(const vector&a) const
{
return (vector){x+a.x,y+a.y};
}
vector operator -(const vector&a) const
{
return (vector){x-a.x,y-a.y};
}
double operator *(const vector&a) const
{
return x*a.y-y*a.x;
}
vector operator *(const double a) const
{
return (vector){a*x,a*y};
}
double len()
{
return sqrt(x*x+y*y);
}
vector rotate()
{
return (vector){-y,x};
}
}a[N],P[N];
struct line
{
dot a;vector p;int i;
bool operator <(const line&a) const
{
return atan2(p.x,p.y)>atan2(a.p.x,a.p.y);
}
}q[N],Q[N];
void addedge(int x,int y){cnt++;edge[cnt].to=y,edge[cnt].nxt=p[x],p[x]=cnt;}
bool onright(line x,dot y)
{
return (y-x.a)*x.p>=;
}
dot cross(line x,line y)
{
return y.a+y.p*(x.p*(x.a-y.a)/(x.p*y.p));
}
int bfs(int S)
{
memset(d,,sizeof(d));
int head=,tail=;queue[]=S;d[S]=;
do
{
int x=queue[++head];
for (int i=p[x];i;i=edge[i].nxt)
if (d[x]+<d[edge[i].to])
{
d[edge[i].to]=d[x]+;
queue[++tail]=edge[i].to;
if (!edge[i].to) return d[edge[i].to];
}
}while (head<tail);
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("3297.in","r",stdin);
freopen("3297.out","w",stdout);
const char LL[]="%I64d";
#else
const char LL[]="%lld";
#endif
T=read();
while (T--)
{
n=read();
int r=read(),c=read();
dot s;s.x=read(),s.y=read();
for (int i=;i<=n;i++)
a[i].x=read(),a[i].y=read();
int S;
double dis=(a[]-s).len();
for (int i=;i<=n;i++) dis=min(dis,(a[i]-s).len());
for (int i=;i<=n;i++) if (fabs(dis-(a[i]-s).len())<eps) S=i;
cnt=;
memset(p,,sizeof(p));
for (int j=;j<=n;j++)
{
int t=;
for (int i=;i<=n;i++)
if (i!=j) q[++t]=(line){(a[i]+a[j])*0.5,(a[i]-a[j]).rotate(),i};
q[++t]=(line){(dot){,},(vector){,},};
q[++t]=(line){(dot){r,},(vector){,},};
q[++t]=(line){(dot){r,c},(vector){-,},};
q[++t]=(line){(dot){,c},(vector){,-},};
sort(q+,q+t+);
int head=,tail=;Q[]=q[];
for (int i=;i<=t;i++)
{
while (head<tail&&onright(q[i],P[tail])) tail--;
while (head<tail&&onright(q[i],P[head+])) head++;
Q[++tail]=q[i];
if (fabs(Q[tail-].p*Q[tail].p)<eps)
{
tail--;
if (onright(q[i],Q[tail].a)) Q[tail]=q[i];
}
if (head<tail) P[tail]=cross(Q[tail],Q[tail-]);
}
while (head<tail&&onright(Q[head],P[tail])) tail--;
P[head]=cross(Q[head],Q[tail]);
for (int i=head;i<=tail;i++) addedge(j,Q[i].i);
}
printf("%d\n",bfs(S));
}
return ;
}