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

时间:2023-03-09 05:40:51
BZOJ3199 SDOI2013 逃考 半平面交、最短路

传送门


如果我们对于每一个点能找到与其相邻的点(即不经过其他点监视范围能够直接到达其监视范围的点)和是否直接到达边界,就可以直接BFS求最短路求出答案。

所以当前最重要的问题是如何找到对于每一个点相邻的点。

如果你知道泰森多边形,你就可以发现所有点的监视范围刚好对应这些点在这个矩形里的泰森多边形。

因为两个点监视范围的分界线一定在这两个点对应线段的中垂线上,所以将当前点到所有点的中垂线拿出来跑一遍半平面交,如果某个点与当前点的中垂线在半平面交中,那么这两个点就相邻。

还需要知道对于某个点能否不经过其他点的监视范围到达边界,这只需要在求半平面交的时候将矩形的四边也加上就可以了。

时间复杂度\(O(TN^2logN)\),可能需要轻微的常数优化。

写计算几何的时候务必注意细节,否则可能因为把\(xy\)坐标打反等小错误调很久QAQ

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
//This code is written by Itst
using namespace std; #define ld long double
const ld eps = 1e-12;
bool cmp(ld a , ld b){return a - eps < b && a + eps > b;} struct comp{
ld x , y , dir;
comp(ld _x = 0 , ld _y = 0) : x(_x) , y(_y){dir = atan2(y , x);}
comp operator +(comp a){return comp(x + a.x , y + a.y);}
comp operator -(comp a){return comp(x - a.x , y - a.y);}
comp operator *(ld a){return comp(x * a , y * a);}
ld operator *(comp a){return x * a.x + y * a.y;}
ld operator %(comp a){return x * a.y - y * a.x;}
bool operator <(const comp a)const{return dir < a.dir;}
bool operator ==(const comp a)const{return cmp(dir , a.dir);}
}now[607]; struct line{
comp pos , dir;
int ind;
line(comp a = comp(0,0) , comp b = comp(0,0) , int id = 0) : pos(a) , dir(b) , ind(id){}
bool operator <(const line a)const{return dir < a.dir || dir == a.dir && ((comp)a.pos - pos) % dir > 0;}
}cur[607]; struct Edge{
int end , upEd;
}Ed[370007];
int head[607] , que[607];
int cntEd , N , X0 , Y0 , X1 , Y1 , hd , tl , T;
bool mrk[607] , vis[607]; inline void addEd(int a , int b){
Ed[++cntEd] = (Edge){b , head[a]};
head[a] = cntEd;
} comp rot(comp a){
ld Cos = 0 , Sin = 1;
return comp(a.x * Cos - a.y * Sin , a.x * Sin + a.y * Cos);
} comp intersect(line a , line b){
ld t = (b.dir % (a.pos - b.pos)) / (a.dir % b.dir);
return a.pos + a.dir * t;
} bool chk(line a , line b , line c){
return (intersect(a , b) - c.pos) % c.dir > 0;
} void create(int x){
int cnt = 0;
cur[++cnt] = line(comp(0 , 0) , comp(1 , 0));
cur[++cnt] = line(comp(X1 , 0) , comp(0 , 1));
cur[++cnt] = line(comp(X1 , Y1) , comp(-1 , 0));
cur[++cnt] = line(comp(0 , Y1) , comp(0 , -1));
for(int i = 1 ; i <= N ; ++i)
if(i != x)
cur[++cnt] = line((now[i] + now[x]) * 0.5 , rot(now[i] - now[x]) , i);
sort(cur + 1 , cur + cnt + 1);
hd = tl = que[1] = 1;
for(int i = 2 ; i <= cnt ; ++i){
if(cur[i].dir == cur[i - 1].dir) continue;
while(hd < tl && chk(cur[que[tl]] , cur[que[tl - 1]] , cur[i]))
--tl;
while(hd < tl && chk(cur[que[hd]] , cur[que[hd + 1]] , cur[i]))
++hd;
que[++tl] = i;
}
while(hd < tl && chk(cur[que[tl]] , cur[que[tl - 1]] , cur[que[hd]]))
--tl;
while(hd <= tl){
if(!cur[que[hd]].ind) mrk[x] = 1;
else addEd(cur[que[hd]].ind , x);
++hd;
}
} int main(){
#ifndef ONLINE_JUDGE
freopen("in","r",stdin);
//freopen("out","w",stdout);
#endif
ios::sync_with_stdio(0);
for(cin >> T ; T ; --T){
cntEd = 0;
memset(mrk , 0 , sizeof(mrk));
memset(vis , 0 , sizeof(vis));
memset(head , 0 , sizeof(head));
cin >> N >> X1 >> Y1 >> X0 >> Y0;
if(!N){cout << "0\n"; continue;}
for(int i = 1 ; i <= N ; ++i)
cin >> now[i].x >> now[i].y;
for(int i = 1 ; i <= N ; ++i)
create(i);
queue < int > q;
ld minDis = 1e18;
int minInd = 0;
for(int i = 1 ; i <= N ; ++i){
ld dis = sqrt((now[i].x - X0) * (now[i].x - X0) + (now[i].y - Y0) * (now[i].y - Y0));
if(dis < minDis){minDis = dis; minInd = i;}
}
vis[minInd] = 1;
q.push(minInd);
bool f = 0;
for(int i = 1 ; !f ; ++i){
for(int j = q.size() ; !f && j ; --j){
int t = q.front(); q.pop();
if(mrk[t]){f = 1; continue;}
for(int i = head[t] ; i ; i = Ed[i].upEd)
if(!vis[Ed[i].end]){
vis[Ed[i].end] = 1;
q.push(Ed[i].end);
}
}
if(f) cout << i << endl;
}
}
return 0;
}