BZOJ 1137: [POI2009]Wsp 岛屿 半平面交

1137: [POI2009]Wsp 岛屿

Time Limit: 10 Sec  Memory Limit: 162 MBSec  Special Judge
Submit: 165  Solved: 78
[Submit][Status][Discuss]

Description

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

Input

第一行正整数n m表示城市数和被控制的岛屿数(3≤n≤10^5 1≤m≤10^6)接下来n行每行两个整数x y表示每个城市的坐标。(|x|,|y|≤10^6)接下来m行描述一条不能走的道路(起点和终点)。数据保证有解。

Output

输出一个实数,最短距离,误差10^-5以内均算正确。

Sample Input

6 9

-12 -10

-11 6

-4 12

6 14

16 6

18 -2

3 4

1 5

2 6

2 3

4 5

3 5

1 3

3 6

1 6

Sample Output

42.000000000

HINT

Source

鸣谢 vfleaking

先瞎BB几句,习惯不看背景的我,一眼看过去以为是求补图的最短路。论审题的重要性。

想法:

根据“道路相交处可以自由穿行。”,然后画图发现。可以把所有合法的路拿出来,把包含1,n的那面作为合法面。这样做一遍半平面交,得到凸多边形的周长就是答案。O(n^2logn)

然后根据“XXX岛屿是一个凸多边形。”,距离最短的合法路肯定是终点编号大的。所以每个点最多只要取一条路就好了。O(nlogn)

#include<cmath>
#include<cstdio>
#include<vector>
#include<algorithm>

const int len(100000);
const double eps(1e-8);
struct Coor//向量或点
{
    double x,y;
    Coor(){};
    Coor(double a,double b):x(a),y(b){};
inline Coor operator +(const Coor&a)const{return Coor(x+a.x,y+a.y);}
inline Coor operator -(const Coor&a)const{return Coor(x-a.x,y-a.y);}
inline Coor operator *(const double&k)const{return Coor(x*k,y*k);}
inline Coor operator /(const double&k)const{return Coor(x/k,y/k);}
}pr[len+10],ie[len+10];int tot;
double dot(Coor a,Coor b){return a.x*b.x+a.y*b.y;}//点积
double cross(Coor a,Coor b){return a.x*b.y-a.y*b.x;}//叉积
struct Line
{
    Coor a,b;//直线上两个点a->b,钦点半平面交均保留向量左手方向。
    double angle;
    Line(){};
    Line(Coor A,Coor B,double a):a(A),b(B),angle(a){};
    Coor intersect(Line &B)
    {
        double s1=cross(B.a-a,B.b-a),s2=cross(B.b-b,B.a-b);//面积的方向
        return a+(b-a)*s1/(s1+s2);
    }
    void getAngle(){angle=atan2(b.y-a.y,b.x-a.x);}
}L[len+10];int up;
int n,m,x,y;long double ans;
std::vector<int>Edge[len+10];
void swap(int &x,int &y){x^=y,y^=x,x^=y;}
int dcmp(double x){return x<-eps?-1:x>eps;}
bool cmp(Line A,Line B)
{
    double c=A.angle-B.angle;
    int d=dcmp(c);
    if(d)return d>0;//逆时针,事实证明这里正着反着都一样。
    c=cross(A.b-A.a,B.b-A.a);//保留左手方向
    return c<-eps;
}
bool jud(Line p1,Line p2,Line p3)
{
    Coor p=p1.intersect(p2);
    double c=cross(p3.b-p3.a,p-p3.a);
    return dcmp(c)<0;
}
void deal()
{
    int _up=1;
    for(int i=2;i<=up;i++)if(dcmp(L[i-1].angle-L[i].angle))L[++_up]=L[i];
    up=_up;
}
long double sqr(long double x){return x*x;}
long double dis(Coor A,Coor B)
{
    return sqrt(sqr(A.x-B.x)+sqr(A.y-B.y));
}
int que[len+10],head,tail;
void SI()
{
    std::sort(L+1,L+1+up,cmp);//排序可能会打乱顺序
    deal();
    que[1]=1;que[2]=2;head=1;tail=2;
    for(int i=3;i<=up;i++)
    {
        while(head<tail && jud(L[que[tail-1]],L[que[tail]],L[i]))
            tail--;
        while(head<tail && jud(L[que[head+1]],L[que[head]],L[i]))
            head++;
        que[++tail]=i;
    }
    while(head<tail-1 && jud(L[que[tail-1]],L[que[tail]],L[que[head]]))
        tail--;
    while(head+1<tail && jud(L[que[head+1]],L[que[head]],L[que[tail]]))
        head++;
    que[tail+1]=que[head];
    for(int i=head;i<=tail;i++)ie[++tot]=L[que[i]].intersect(L[que[i+1]]);
    ie[tot+1]=ie[1];
    for(int i=1;i<=tot;i++)ans+=dis(ie[i],ie[i+1]);
    ans-=dis(pr[1],pr[n]);
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%lf %lf",&pr[i].x,&pr[i].y);
    for(int i=1;i<=m;i++)
    {
        scanf("%d %d",&x,&y);
        if(x>y)swap(x,y);
        Edge[x].push_back(y);
    }
    for(int i=1,j;i<n;i++)
    {
        std::sort(Edge[i].begin(),Edge[i].end());
        j=n;
        while(Edge[i].size()&&Edge[i].back()==j&&j>i)
        j--,Edge[i].pop_back();
        if(j>i)L[++up]=Line(pr[j],pr[i],0);//逆时针
        if(i==1&&j==n)ans=dis(pr[1],pr[n]);
    }
    if(ans){printf("%.10Lf\n",ans);return 0;}
    L[++up]=Line(pr[1],pr[n],0);
    for(int i=1;i<=up;i++)L[i].getAngle();
    SI();
    printf("%.10Lf\n",ans);
    return 0;
}
时间: 2024-10-12 12:39:47

BZOJ 1137: [POI2009]Wsp 岛屿 半平面交的相关文章

BZOJ 1038 ZJOI2008 瞭望塔 半平面交

题目大意及模拟退火题解:见 http://blog.csdn.net/popoqqq/article/details/39340759 这次用半平面交写了一遍--求出半平面交之后.枚举原图和半平面交的每一个点,求出答案就可以 #include<cmath> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define M 310 #define

BZOJ 2618 CQOI 2006 凸多边形 半平面交

题目大意:给出n个凸多边形,求这些多边形的面积的交. 思路:犯傻了..以后看到凸多边形第一时间就要想到半平面交啊..多明显啊,半天愣着没想出来. CODE: #include <cmath> #include <cstdio> #include <iomanip> #include <cstring> #include <iostream> #include <algorithm> #define MAX 6100 #define E

BZOJ 2732 HNOI 2012 射箭 半平面交

题目大意:给出一些与x轴垂直的线段,问一个经过原点的抛物线最多能按顺序经过多少条线段. 思路:总体上来说是数学题,我们来推一推. 设这个经过原点的抛物线为y = a * x ^ 2 + b * x,设一条线段的起点和终点为(x0,y1)和(x0,y2),且y2 > y1. 将x0带入到设出的抛物线中,会得到y = a * x0 ^ 2 + b * x0,这时候需要满足的是y <= y2 && y >= y1,也就是a * x0 ^ 2 + b * x0 <= y2

BZOJ 1038 ZJOI 2008 瞭望塔 半平面交

题目大意:给出一个村庄的轮廓,在这个村庄里可以在随意的地方建一个瞭望塔.这个塔须要足够高,使得可以看得村庄的全貌. 求这个瞭望塔的最小高度. 思路:对于村庄中的每一条边,瞭望塔为了看见它.必需要在这个直线左側的半平面区域.这种话为了满足全部的边的需求,做一次半平面交,瞭望塔的最高点必须在全部边的半平面交的区域内. 例如以下图例子. 全部边的半平面交区域就是上面的图形.设上面半平面的函数关系是F(x).村长的函数关系是G(x),那么问题就转化成了求一个x,使得(F(x) - G(x))最小. 这个

HDU 2297 半平面交

Run Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 640    Accepted Submission(s): 181 Problem Description Since members of Wuhan University ACM Team are lack of exercise, they plan to particip

关于半平面交

嗯,这是一个很屌的东西.可以把他想象成数学中的线性规划问题,然后自然而然得想到就可以求最优解啦. 如何求解半平面交????? 做法一:暴力枚举点,用该点切割现有的凸多边形,这样的复杂度是O(n^2) 做法二:神奇的分治O(nlogn),然而我并不会.... 做法三:参见2006年朱泽园大神发明的排序增量法:这里就暂时不给详细介绍了. 复杂度O(nlogn),如果把快速排序改成基数排序,复杂度可以进一步降为:O(n)!!!!!! 半平面交的另外一个应用是求多边形的核:可以想象成站在一个多边形的点上

计算几何 半平面交

LA 4992 && hdu 3761 Jungle Outpost 杭电的有点坑啊..一直爆内存,后来发现大白的半平面交模板那里 point *p = new point[n]; line *q = new line[n]这里出了问题,应该是在函数里面申请不了比较大的数组,所以爆内存..我在全局定义了两个数组就不会爆了.. 本来跑了17s多的,后来半平面交sort( l, l + n ) 被我注释了,就跑了9s多,LA上跑了 2s..应该是输入数据比较好,不用按照极角排序..然后就是照着

POJ3525 半平面交

题意:求某凸多边形内部离边界最远的点到边界的距离 首先介绍半平面.半平面交的概念: 半平面:对于一条有向直线,它的方向的左手侧就是它所划定的半平面范围.如图所示: 半平面交:多个半平面的交集.有点类似二元函数的线性规划.如图 求半平面交:用的kuangbin模板= = sol:二分答案 二分距离值,按这个值把边界向内缩,再求新的半平面交.如图: 绿色的是原图形,蓝色是按距离值向里面缩进去之后得到的新图形.对这个新图做半平面交即可. 若半平面交存在,说明与边界的距离是该值的点存在(半平面交里面的点

bzoj2618[Cqoi2006]凸多边形 半平面交

这是一道半平面交的裸题,第一次写半平面交,就说一说我对半平面交的理解吧. 所谓半平面交,就是求一大堆二元一次不等式的交集,而每个二元一次不等式的解集都可以看成是在一条直线的上方或下方,联系直线的标准方程就可以得出.于是乎这些不等式就可以转化为一些半平面,求的就是半平面交. 而半平面交不可能交出凹多边形(因为凹多边形的定义是有一条边所在的直线能把该多边形分成若干块...YY一下就知道这是不可能的),这是一个十分优美的性质,正类似于凸包(写法也是有些相似的),但半平面交可能交出无界,于是可以加四条类