首页 > 代码库 > BZOJ 1137: [POI2009]Wsp 岛屿 半平面交

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;
}

 

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