BZOJ 1137: [POI2009]Wsp 岛屿 半平面交
Posted Oncle_Ha
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ 1137: [POI2009]Wsp 岛屿 半平面交相关的知识,希望对你有一定的参考价值。
1137: [POI2009]Wsp 岛屿
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
-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
先瞎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 岛屿 半平面交的主要内容,如果未能解决你的问题,请参考以下文章