瞭望塔-半平面交
题目描述
题解
首先维护出所有直线的半平面交(注意加边界),所有直线的半平面交是一个下凸壳,然后发现瞭望塔的横坐标一定是下凸壳中顶点的横坐标,或原村庄点的横坐标,O(n)扫一遍更新答案
代码实现(一直90分,不知哪里挂了)
#include<bits/stdc++.h>
#define M 100009
using namespace std;
int tot,cnt,num,n;
double ans=1e+17;//ans初始值尽量大(坑点)
const double eps=1e-10;//注意精度
struct point{
double x,y;
point(double a=0,double b=0){x=a,y=b;}
friend inline point operator+(const point &a,const point &b){return point(a.x+b.x,a.y+b.y);}
friend inline point operator-(const point &a,const point &b){return point(a.x-b.x,a.y-b.y);}
friend inline double operator*(const point &a,const point &b){return a.x*b.y-b.x*a.y;}
friend inline double operator^(const point &a,const point &b){return a.x*b.x+a.y*b.y;}
friend inline point operator/(const point &a,const double &b){return point(a.x/b,a.y/b);}
friend inline point operator%(const point &a,const double &b){return point(a.x*b,a.y*b);}
inline double dist(){return sqrt(x*x+y*y);}
}p[M],q[M],w[M];
struct line{
point a,b;
double slop;
}l[M],s[M];
bool cmp(const line &a,const line &b){
if(fabs(a.slop-b.slop)>eps) return a.slop+eps<b.slop;
else return (a.b-a.a)*(b.b-a.a)<eps;
}
bool comp(const point &a,const point &b){return a.x+eps<b.x;}
bool jud(point y,line x){return (y-x.a)*(x.b-x.a)>eps;}
point inter(line a,line b){
double k1,k2,t;
k1=(b.b-a.a)*(a.b-a.a);
k2=(a.b-a.a)*(b.a-a.a);
t=k1/(k1+k2);
return point(b.b.x+(b.a.x-b.b.x)*t,b.b.y+(b.a.y-b.b.y)*t);
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%lf",&p[i].x);
for(int i=1;i<=n;i++) scanf("%lf",&p[i].y);
for(int i=1;i<n;i++) l[++cnt].a=p[i],l[cnt].b=p[i+1];
l[++cnt].b=point(p[1].x,1e+12),l[cnt].a=point(p[n].x,1e+12);
l[++cnt].b=p[1],l[cnt].a=point(p[1].x,1e+12);
l[++cnt].a=p[n],l[cnt].b=point(p[n].x,1e+12); //加边界
for(int i=1;i<=cnt;i++) l[i].slop=atan2(l[i].b.y-l[i].a.y,l[i].b.x-l[i].a.x);
sort(l+1,l+cnt+1,cmp);
for(int i=1;i<=cnt;i++){
if(l[i].slop!=l[i-1].slop) tot++;
l[tot]=l[i];
}cnt=tot;tot=0;//去掉斜率相同而无用的直线
int L=1,R=0;
for(int i=1;i<=cnt;i++){
while(R>L&&jud(q[R-1],l[i])) R--;
while(R>L&&jud(q[L],l[i])) L++;
s[++R]=l[i];
if(R>L) q[R-1]=inter(s[R],s[R-1]);
}
while(R>L&&jud(q[R-1],s[L])) R--;
//while(R>L&&jud(q[L],s[R])) L++;
if(R>L) q[R]=inter(s[R],s[L]);
for(int i=L;i<=R;i++) w[++tot]=q[i];
sort(w+1,w+tot+1,comp);
num=1;
for(int i=2;i<=tot;i++)
if(w[i].y<1e+11) w[++num]=w[i];//去掉边界点
int w1=1,w2=2;
p[0].x=p[1].x-1;
w[0].x=w[1].x-1;
while (w1<=num||w2<=n){//O(n)扫一遍更新答案
if (w2>n||(w1<=num&&w[w1].x<p[w2].x)){
point t=p[w2-1]+(p[w2]-p[w2-1])/(p[w2].x-p[w2-1].x)%(w[w1].x-p[w2-1].x);
ans=min(ans,w[w1].y -t.y);
w1++;
}
else{
point t=w[w1-1]+(w[w1]-w[w1-1])/(w[w1].x-w[w1-1].x)%(p[w2].x-w[w1-1].x);
ans = min(ans,t.y-p[w2].y);
w2++;
}
}printf("%.3lf\n",ans);
return 0;
}