BZOJ1137:[POI2009]Wsp 岛屿(半平面交)

传送门

Byteotia岛屿是一个凸多边形。城市全都在海岸上。按顺时针编号1到n。任意两个城市之间都有一条笔直的道路相连。道路相交处可以自由穿行。有一些道路被游击队控制了,不能走,但是可以经过这条道路与未被控制的道路的交点。问从城市1到n的最短距离。

题解:
首先凸多边形中1到n的最短路可以用半平面交做,发现一个点出发的直线只有可能是最后一条在半平面交中存在,所以用最后一条直接做半平面交就好了。

#include<bits/stdc++.h>
using namespace std;
inline int read(){
    char ch=getchar();int i=0,f=1;
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch)){i=(i<<1)+(i<<3)+ch-'0';ch=getchar();}
    return i*f;
}

const int Maxn=1e5+50;
const double eps=1e-9;
inline int sgn(double x){return (x>eps)-(x<-eps);}
struct point{
    double x,y;
    point(){}
    point(double x,double y):x(x),y(y){}
    friend inline point operator -(const point &a,const point &b){return point(a.x-b.x,a.y-b.y);}
    friend inline double operator *(const point &a,const point &b){return a.x*b.y-a.y*b.x;}
    inline double norm(){return sqrt(x*x+y*y);}
}p[Maxn];
struct line{
    point a,b;
    double sp;
    line(){}
    line(point a,point b):a(a),b(b){}
}l[Maxn];
int n,m,tot;
vector<int>ban[Maxn];
inline bool operator <(const line &a,const line &b){
    int t=sgn(a.sp-b.sp);
    if(t)return t<0;
    else return (b.b-a.a)*(b.a-a.a)>=0;
}
inline point insert(const line &a,const line &b){
    double k1=(a.b-b.a)*(a.a-b.a);
    double k2=(a.a-b.b)*(a.b-b.b);
    #define t k1/(k1+k2)
    return point(b.a.x+(b.b.x-b.a.x)*t,b.a.y+(b.b.y-b.a.y)*t);
    #undef t
}
inline bool judge(const line &c,const line &a,const line &b){
    point q=insert(a,b);
    return (c.a-q)*(c.b-q)<eps; 
}
inline void hpi(){
    for(int i=1;i<=tot;i++)l[i].sp=atan2(l[i].b.y-l[i].a.y,l[i].b.x-l[i].a.x);
    sort(l+1,l+tot+1);m=0;
    for(int i=1;i<=tot;i++){
        if(i==1||sgn(l[i].sp-l[i-1].sp))++m;
        l[m]=l[i];
    }
    int L=1,R=2;
    for(int i=3;i<=m;i++){
        while(L<R&&judge(l[i],l[R-1],l[R]))--R;
        while(L<R&&judge(l[i],l[L],l[L+1]))++L;
        l[++R]=l[i];
    }
    while(L<R-1&&judge(l[R],l[L],l[L+1]))++L;
    while(L<R-1&&judge(l[L],l[R-1],l[R]))--R;
    double ans=-(p[n]-p[1]).norm();tot=0;l[++R]=l[L];
    for(int i=L;i<R;++i)p[++tot]=insert(l[i],l[i+1]);
    for(int i=1;i<tot;i++)ans+=(p[i+1]-p[i]).norm();
    ans+=(p[1]-p[tot]).norm();
    printf("%.10f\n",ans); 
}
int main(){
    n=read(),m=read();
    for(int i=1;i<=n;i++)p[i].x=read(),p[i].y=read();
    for(int i=1;i<=m;i++){
        int x=read(),y=read();
        ban[min(x,y)].push_back(max(x,y));
    }
    for(int i=1;i<=n;i++){
        if(ban[i].size())sort(ban[i].begin(),ban[i].end()); 
        int t=ban[i].size()-1,o=n;
        while(t>=0&&ban[i][t]==o)--o,--t;
        if(o>i)l[++tot]=line(p[o],p[i]);
    }l[++tot]=line(p[1],p[n]);
    hpi();
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值