[半平面交][最短路]JZOJ 3297 【SDOI2013】逃考
分析
我们可以发现两个亲戚之间的监视距离分界线就是他们的连线的中垂线
那么一个亲戚的监视范围就是他与其他亲戚的连线的中垂线围成的凸多边形
这就是半平面交啦
那么我们轻松3小时+地打完半平面交模板以后,我们可以发现题目要求的就是小杨经过的路线穿过半平面交的最少次数,则使半平面交相邻的亲戚连边权为1的边,跑最短路即可
#include <iostream> #include <cstdio> #include <cmath> #include <algorithm> #include <queue> #include <memory.h> using namespace std; typedef double ld; const int N=6e2+10; const ld eps=1e-12; int dcmp(ld a) {return a<-eps?-1:(a>eps?1:0);} struct Point { ld x,y; }s,t,a[N]; struct Line { Point p,v; ld angle; int id; friend bool operator < (Line a,Line b) {return dcmp(a.angle-b.angle)<0;} }l[N]; int lcnt; struct Graph { int v,nx; }g[4*N*N]; int cnt,list[N],dis[N]; bool vis[N]; int T,n; Point operator + (Point a,Point b) {return (Point){a.x+b.x,a.y+b.y};} Point operator - (Point a,Point b) {return (Point){a.x-b.x,a.y-b.y};} Point operator * (Point a,ld b) {return (Point){a.x*b,a.y*b};} Point operator / (Point a,ld b) {return (Point){a.x/b,a.y/b};} ld Cross_Multi(Point a,Point b) {return a.x*b.y-a.y*b.x;} ld Dot_Multi(Point a,Point b) {return a.x*b.x+a.y*b.y;} Point Mid_Point(Point a,Point b) {return (a+b)/2.0;} Point Rotate(Point v) {return (Point){-v.y,v.x};} bool Left(Line l,Point b) {return Cross_Multi(l.v,b-l.p)>0;} void Add(int u,int v) { g[++cnt]=(Graph){v,list[u]};list[u]=cnt; g[++cnt]=(Graph){u,list[v]};list[v]=cnt; } void Pre_Process(int k) { lcnt=0; l[++lcnt]=(Line){(Point){0,t.y},(Point){0,-1},atan2(-1,0),n+1}; l[++lcnt]=(Line){(Point){0,0},(Point){1,0},atan2(0,1),n+1}; l[++lcnt]=(Line){(Point){t.x,0},(Point){0,1},atan2(1,0),n+1}; l[++lcnt]=(Line){t,(Point){-1,0},atan2(0,-1),n+1}; for (int i=1;i<=n;i++) if (i!=k) { Point mid=Mid_Point(a[i],a[k]),rt=Rotate(a[i]-a[k]); l[++lcnt]=(Line){mid,rt,atan2(rt.y,rt.x),i}; } int x=lcnt; sort(l+1,l+lcnt+1); } Point Linecut(Line a,Line b) { Point w=a.p-b.p; ld t=Cross_Multi(b.v,w)/Cross_Multi(a.v,b.v); return a.p+a.v*t; } void Halfcut(int x) { deque<Line> q; deque<Point> p; while (!q.empty()) q.pop_front(); while (!p.empty()) p.pop_front(); q.push_front(l[1]); for (int i=2;i<=lcnt;i++) { while (!p.empty()&&!Left(l[i],p.back())) q.pop_back(),p.pop_back(); while (!p.empty()&&!Left(l[i],p.front())) q.pop_front(),p.pop_front(); if (dcmp(Cross_Multi(l[i].v,q.back().v))==0) { if (Left(q.back(),l[i].p)) { q.pop_back(); if (!q.empty()) { p.pop_back(); p.push_back(Linecut(q.back(),l[i])); } q.push_back(l[i]); } } else p.push_back(Linecut(q.back(),l[i])),q.push_back(l[i]); } while (!p.empty()&&!Left(q.front(),p.back())) q.pop_back(),p.pop_back(); if (p.empty()) return; while (!q.empty()) { Add(x,q.front().id); q.pop_front(); } } void BFS(int v0) { queue<int> q; memset(dis,0x3f,sizeof dis);memset(vis,0,sizeof vis); while (!q.empty()) q.pop(); q.push(v0);dis[v0]=0;vis[v0]=1; while (!q.empty()) { int u=q.front();q.pop(); for (int i=list[u];i;i=g[i].nx) if (dis[g[i].v]>dis[u]+1) { dis[g[i].v]=dis[u]+1; if (!vis[g[i].v]) q.push(g[i].v); vis[g[i].v]=1; } vis[u]=0; } printf("%d\n",dis[n+1]); } int main() { for (scanf("%d",&T);T;T--) { scanf("%d",&n); scanf("%lf%lf%lf%lf",&t.x,&t.y,&s.x,&s.y); if (!n) { printf("0\n"); continue; } int mx=2147483647,st; for (int i=1;i<=n;i++) { scanf("%lf%lf",&a[i].x,&a[i].y); if (mx>Dot_Multi(a[i]-s,a[i]-s)) { mx=Dot_Multi(a[i]-s,a[i]-s); st=i; } } cnt=0;memset(list,0,sizeof list); for (int i=1;i<=n;i++) { Pre_Process(i); Halfcut(i); } BFS(st); } }
在日渐沉没的世界里,我发现了你。