1. 程式人生 > >[BZOJ3199][Sdoi2013]escape:半平面交

[BZOJ3199][Sdoi2013]escape:半平面交

分析

好像叫V圖什麼的。

容易發現,對於每個點,其監視的範圍就是這個點與其它所有點的垂直平分線分割平面後的半平面交。由於資料範圍很小,所以我們可以直接列舉每個點,使用雙端佇列求出其監視的範圍。若兩個點的監視範圍有公共邊,那麼就在這兩個點之間連一條邊,邊權為\(1\)。然後從起點bfs一遍即可。

這裡重點說一下求半平面交的細節,畢竟這是ErkkiErkko這個大菜雞第一次寫半平面交。

可以使用有向的直線,規定直線左側的平面是合法的區域。

求兩條有向直線的交點時,可以使用面積作為中間量進行轉換,具體請參見程式碼。

如果兩條直線方向相同,選更靠左的那一條,畢竟咱們求的是半平面交而不是半平面並,

說到半平面並,感覺可以通過高中數學裡交補補並並補補交那套東西轉化成半平面交來求解。

還有一個很重要的地方,因為是雙端佇列(或許也可以理解為,半平面交一般是圍成一圈),所以在插入最後一條有向線段之後,要檢查隊尾兩條直線的交點是否滿足隊首的直線,如果不滿足就彈出隊尾,直到滿足為止。

半平面交進行極角排序的時候,直接用叉積來排的話會出很多奇奇怪怪的鍋(因為叉積不滿足偏序,大概吧,說錯了不要噴ErkkiErkko,畢竟他這麼菜),所以可以呼叫std::atan2()函式。這裡最好預處理出來,而在cmp()函式中呼叫,否則會對常數有很大的影響(大概差\(7,8\)倍的樣子,可怕)。

\(n\)

對應的是橫座標不是縱座標。

更具體的就看程式碼吧。

程式碼

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <cctype>
#include <algorithm>
#include <queue>
#define rin(i,a,b) for(int i=(a);i<=(b);i++)
#define rec(i,a,b) for(int i=(a);i>=(b);i--)
#define trav(i,a) for(int i=head[(a)];i;i=e[i].nxt)
typedef long long LL;
using std::cin;
using std::cout;
using std::endl;

inline int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return x*f;
}

const int MAXN=605;
int n,lcnt,hd,tl,ecnt,head[MAXN];
int s,dis[MAXN];
double R,C,sx,sy;
bool vis[MAXN],invalid[MAXN];
struct Edge{
    int to,nxt;
}e[MAXN*MAXN];

inline void add_edge(int bg,int ed){
    ecnt++;
    e[ecnt].to=ed;
    e[ecnt].nxt=head[bg];
    head[bg]=ecnt;
}

struct Po{
    double x,y;
    inline friend Po operator + (Po A,Po B){return (Po){A.x+B.x,A.y+B.y};}
    inline friend Po operator - (Po A,Po B){return (Po){A.x-B.x,A.y-B.y};}
    inline friend Po operator * (Po A,double B){return (Po){A.x*B,A.y*B};}
    inline friend Po operator / (Po A,double B){return (Po){A.x/B,A.y/B};}
}re[MAXN];
inline int dcmp(double A,double B){return fabs(A-B)<1e-8?0:((A>B)*2)-1;}
inline Po getmid(Po A,Po B){return (Po){(A.x+B.x)/2,(A.y+B.y)/2};}
typedef Po Ve;
inline double getlen(Ve A){return sqrt(A.x*A.x+A.y*A.y);}
inline double getcross(Ve A,Ve B){return A.x*B.y-A.y*B.x;}
inline Ve getnor(Ve A){return (Ve){-A.y,A.x}/getlen(A);}
struct Li{
    Po u;Ve v;int id;double ang;
    inline friend bool operator < (Li A,Li B){return dcmp(A.ang,B.ang)<0;}
    inline Po getp(double t){return u+v*t;}
}l[MAXN],que[MAXN];
inline Po getinter(Li A,Li B){return A.getp(getcross(B.v,A.u-B.u)/getcross(A.v,B.v));}
inline Li getpb(Po A,Po B,int id){return (Li){getmid(A,B),getnor(B-A),id,0};}
inline bool isleft(Po A,Li B){return dcmp(getcross(B.v,A-B.u),0)>0;}

inline void solve(int x){
    lcnt=0;
    l[++lcnt]=(Li){(Po){0,0},(Ve){1,0},n+1,0},l[lcnt].ang=std::atan2(l[lcnt].v.y,l[lcnt].v.x);
    l[++lcnt]=(Li){(Po){C,0},(Ve){0,1},n+1,0},l[lcnt].ang=std::atan2(l[lcnt].v.y,l[lcnt].v.x);
    l[++lcnt]=(Li){(Po){C,R},(Ve){-1,0},n+1,0},l[lcnt].ang=std::atan2(l[lcnt].v.y,l[lcnt].v.x);
    l[++lcnt]=(Li){(Po){0,R},(Ve){0,-1},n+1,0},l[lcnt].ang=std::atan2(l[lcnt].v.y,l[lcnt].v.x);
    rin(i,1,n) if(!invalid[i]) if(i!=x) l[++lcnt]=getpb(re[x],re[i],i),l[lcnt].ang=std::atan2(l[lcnt].v.y,l[lcnt].v.x);
    std::sort(l+1,l+lcnt+1);
    hd=1,tl=0;
    rin(i,1,lcnt){
        while(hd<tl&&!isleft(getinter(que[tl-1],que[tl]),l[i])) tl--;
        while(hd<tl&&!isleft(getinter(que[hd],que[hd+1]),l[i])) hd++;
        if(hd<=tl&&dcmp(getcross(l[i].v,que[tl].v),0)==0){
            if(isleft(l[i].u,que[tl])) que[tl]=l[i];
        }
        else que[++tl]=l[i];
    }
    while(hd<tl&&!isleft(getinter(que[tl-1],que[tl]),que[hd])) tl--;
    rin(i,hd,tl) add_edge(x,que[i].id);
}

std::queue<int> bfq; 
inline void bfs(){
    memset(dis,0x3f,sizeof dis);
    memset(vis,0,sizeof vis);
    while(!bfq.empty()) bfq.pop();
    vis[s]=1,dis[s]=1,bfq.push(s);
    while(!bfq.empty()){
        int x=bfq.front();bfq.pop();
        trav(i,x){
            int ver=e[i].to;
            if(vis[ver]) continue;
            vis[ver]=1,dis[ver]=dis[x]+1;
            if(ver==n+1) return;
            bfq.push(ver);
        }
    }
}

int main(){
    int T=read();
    while(T--){
        s=0;
        ecnt=0;memset(head,0,sizeof head);
        memset(invalid,0,sizeof invalid);
        n=read();
        C=read(),R=read(),sx=read(),sy=read();
        if(n==0){printf("0\n");continue;}
        rin(i,1,n){
            re[i].x=read(),re[i].y=read();
            if(re[i].x>=C||re[i].y>=R) invalid[i]=1;
        }
        rin(i,1,n) if(!invalid[i]) solve(i);
        rin(i,1,n) if(!invalid[i]) if(!s||dcmp(getlen(re[i]-(Po){sx,sy}),getlen(re[s]-(Po){sx,sy}))<0) s=i;
        bfs();
        printf("%d\n",dis[n+1]-1);
    }
    return 0;
}