bzoj1137 [POI2009]Wsp 岛屿 半平面交

题目大意:
有一个n个点的凸多边形,任意两个点之间有一条笔直的路径,可以在路径相交的时候换路。
现在有m条路不能走了,问从点1走到点n的最短路是多少。

题目分析:
这道题其实是让求一个剩余路的半平面交的周长(这到底是怎么想到的orz)。
但是路有n^2条,但是对于一个点,最前面的一条边可以把后面的所有边都弹掉,所以后面那些边都没有用了,只加最前面的一条边就可以了,于是就变成边数就变成了n。

把n到1这条加进来,在统计答案的时候再把这条边减去即可。

代码如下:

#include <cstdio>
#include <cmath>
#include <algorithm>
#define N 1200000
using namespace std;
const double eps=1e-7;
inline double sqr(double x) { return x*x; }
int n,m,top;
pair<int,int> l[N];
int tmp[N];
struct point{
    double x,y;
    point(){}
    point(double x,double y):x(x),y(y){}
    point operator + (const point &c) const { return point(x+c.x,y+c.y); }
    point operator - (const point &c) const { return point(x-c.x,y-c.y); }
    double operator * (const point &c) const { return x*c.y-y*c.x; }
    double operator | (const point &c) const { return sqrt(sqr(x-c.x)+sqr(y-c.y)); }
    point operator * (const double &c) const { return point(x*c,y*c); }
}b[N],h[N];
struct line{
    point p,v;
    double alpha;
    line(){}
    line(point a,point b):p(a),v(b-a) { alpha=atan2(v.y,v.x); }
    bool operator < (const line &c) const { return alpha<c.alpha; }
    point operator ^ (const line &c) const
    {
        point tmp=p-c.p;
        double rate=(c.v*tmp)/(v*c.v);
        return p+v*rate;
    }
}a[N],p[N];
bool onleft(point c,line l)
{
    point tmp=c-l.p;
    return l.v*tmp>=0;
}
double half_plane_intersection()
{
    sort(a+1,a+1+top);
    int l=1,r=1;
    for(int i=1;i<=top;i++)
    {
        while(r-l>=2 && !onleft(p[r-1]^p[r-2],a[i])) r--;
        if(r-l>=1 && fabs(p[r-1].v*a[i].v)<=0)
            p[r-1]=onleft(a[i].p,p[r-1])?a[i]:p[r-1];
        else p[r++]=a[i];
    }
    for(;;)
    {
             if(r-l>=2 && !onleft(p[r-1]^p[r-2],p[l])) r--;
        else if(r-l>=2 && !onleft(p[l]^p[l+1],p[r-1])) l++;
        else break;
    }
    p[r]=p[l];
    for(int i=l;i<r;i++)
        h[i]=p[i]^p[i+1];
    h[r]=h[l];
    double ans=0;
    for(int i=l;i<r;i++)
        ans+=h[i]|h[i+1];
    return ans;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%lf%lf",&b[i].x,&b[i].y);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&l[i].first,&l[i].second);
        if(l[i].first>l[i].second) swap(l[i].first,l[i].second);
    }
    sort(l+1,l+1+m);
    for(int i=1;i<=n;i++) tmp[i]=1;
    for(int i=1;i<=m;i++)
        if(tmp[l[i].second]==l[i].first) tmp[l[i].second]++;
    if(tmp[n]==1)
    {
        printf("%lf",b[1]|b[n]);
        return 0;
    }
    for(int i=1;i<=n;i++)
        if(tmp[i]<i) a[++top]=line(b[i],b[tmp[i]]);
    a[++top]=line(b[1],b[n]);
    printf("%lf\n",half_plane_intersection()-(b[1]|b[n]));
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值