[半平面交][最短路]JZOJ 3297 【SDOI2013】逃考

Description

高考又来了,对于不认真读书的来讲真不是个好消息。为了小杨能在家里认真读书,他的亲戚决定驻扎在他的家里监督他学习,有爷爷奶奶、外公外婆、大舅、大嫂、阿姨……
小杨实在是忍无可忍了,这种生活跟监狱有什么区别!为了他亲爱的小红,为了他的dota,他决定越狱!
假设小杨的家是个n*m 的矩阵,左下角坐标为(0,0),右上角坐标为(x1,y1)。小杨有n 个亲戚,驻扎在矩阵里(位置不同,且不在矩阵的边上)。小杨家里的每个地方都被亲戚监控着,而且只被距离最近的亲戚监控:
也就是说假设小杨所在的位置是(3,3),亲戚A 在(3,0),A 距离小杨距离是3;亲戚B 在(6,7),则B 距离小杨距离是5。距离A < 距离B,所以(3,3)位置由A 监控。
如果“最近距离”出现同时有几个亲戚,那么那个位置同时被那几个亲戚监控。
给出小杨的坐标(x0,y0)。因为被发现的人数越少,越狱成功的机会越大,所以小杨需要你设计一条越狱路线到达矩形的边上,且被发现的人数最少。
Ps:小杨做的方向是任意的,也就是说路线上的任意位置只需要是实数。
保证一开始小杨只被一个亲戚监控着。
 

Input

第一行,一个正整数t<=3,表示数据个数。
接下来t 个数据:
    第一行n,表示小杨的亲戚个数。
    接下来一行四个正整数,表示矩形右上角的坐标(x1,y1)和小杨的坐标(x0,y0)。
    接下来n 行,每行两个正整数,代表一个亲戚的位置。

Output

每个数据输出一个正整数,表示小杨越狱被发现人数的最小值。
 

Sample Input

3
4
10 10 5 5
5 6
3 5
7 5
5 3
0
10 10 5 5
3
3 3 2 2
1 1
2 2
2 1

Sample Output

1
0
1
 

Data Constraint

前50%数据,n<=200;
其余数据n<=600。
 

Hint

样例解释:
第一个数据,小杨直接往上走,只被(5,6)监控过。
第二个数据,小杨被(7,7)监控,走到(9,9)被(7,11)监控,然后直接往上走。

分析

我们可以发现两个亲戚之间的监视距离分界线就是他们的连线的中垂线

那么一个亲戚的监视范围就是他与其他亲戚的连线的中垂线围成的凸多边形

这就是半平面交啦

那么我们轻松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);
    }
}
View Code

 

posted @ 2019-06-29 20:47  Vagari  阅读(171)  评论(0编辑  收藏  举报