题目大意:平面上给出N个岛屿形成一个凸多边形,两两岛屿之间有边相连,道路相交处视为分岔路口。现在有M条道路毁坏了,但是道路相交处仍可正常通行,求从1到N的最短距离
挺厉害的题...
首先由于是一个凸多边形,所以最优方案是沿着"完好无损的那些道路的半平面交"来走
(画个图感受一下还是挺明显的)
但是直接做是不行的,因为有O(N^2)个限制
所以我们考虑去掉一些无用的半平面...
还是由于这是一个凸多边形,所以对于一个点i,只有和他相连的标号最大的那个点与他形成的半平面是有用的,所以我们对于每个点只保留一个(i,last[i])的半平面就好了
这样半平面个数就降为了O(N)的
直接上半平面交就可以了
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<cmath>
#define N 100010
using namespace std;
vector<int>V[N];
int last[N];
struct node{double x,y;};
struct line{node a,b;double ang;};
node operator +(const node &x,const node &y){return (node){x.x+y.x,x.y+y.y};}
node operator -(const node &x,const node &y){return (node){x.x-y.x,x.y-y.y};}
node operator *(const node &x,const double &y){return (node){x.x*y,x.y*y};}
double xj(node x,node y){return x.x*y.y-x.y*y.x;}
bool cmp(line x,line y)
{
if(x.ang!=y.ang) return x.ang<y.ang;
return xj(y.b-x.a,y.a-x.a)>0;
}
double sqr(double x){return x*x;}
double dis(node x,node y){return sqrt(sqr(y.x-x.x)+sqr(y.y-x.y));}
bool left(node x,line y){return xj(y.a-x,y.b-x)>=0;}
void print(node x){cout<<x.x<<' '<<x.y<<' ';}
void print(line x){print(x.a);cout<<'*';print(x.b);cout<<endl;}
node jiao(line x,line y)
{
double b=xj(x.b-x.a,y.a-x.a)/xj(y.b-y.a,x.b-x.a);
return y.a+(y.b-y.a)*b;
}
node a[N];
line L[N];
line q[N];
int h,t;
node p[N];
int main()
{
int n,m;
scanf("%d%d",&n,&m);
int i,j,x,y;
for(i=1;i<=n;i++)
scanf("%lf%lf",&a[i].x,&a[i].y);
for(i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
V[x].push_back(y);V[y].push_back(x);
}
for(i=1;i<=n;i++)
{
sort(V[i].begin(),V[i].end());
int s=V[i].size();
for(j=s-1;j>=0;j--)
{
if(V[i][j]!=n-s+j+1)
{
last[i]=n-s+j+1;
break;
}
}
if(j==-1) last[i]=n-s;
}
if(last[1]==n) {printf("%.9lf",dis(a[1],a[n]));return 0;}
int cnt=0;
for(i=1;i<=n;i++)
if(last[i]>i)
{
cnt++;
L[cnt].a=a[i];
L[cnt].b=a[last[i]];
L[cnt].ang=atan2(L[cnt].b.y-L[cnt].a.y,L[cnt].b.x-L[cnt].a.x);
}
cnt++;
L[cnt].a=a[n];
L[cnt].b=a[1];
L[cnt].ang=atan2(L[cnt].b.y-L[cnt].a.y,L[cnt].b.x-L[cnt].a.x);
sort(L+1,L+cnt+1,cmp);
int tot=0;
L[0].ang=-707;
for(i=1;i<=cnt;i++)
if(L[i].ang!=L[i-1].ang)
tot++,L[tot]=L[i];
cnt=tot;
h=t=1;q[1]=L[1];
for(i=2;i<=cnt;i++)
{
while(h<t&&left(p[t-1],L[i])) t--;
while(h<t&&left(p[h],L[i])) h++;
t++;q[t]=L[i];
if(h<t) p[t-1]=jiao(q[t-1],q[t]);
}
while(h<t&&left(p[t-1],q[h])) t--;
p[t]=jiao(q[t],q[h]);
t++;p[t]=p[h];
double ans=0;
for(i=h;i<t;i++)
ans+=dis(p[i],p[i+1]);
printf("%.9lf",ans-dis(a[1],a[n]));
}