大灾变
Description
艾泽拉斯世界经历一场亘古未有的地震过后,大地和海洋被完全撕裂,旧大陆残缺不全。联盟和部落各种族的居民们被迫离开了世代居住的家园,来寻找新的生存空间。原本平坦的陆地上现在隆起了一座座山峰,暴风城的人类开始在艾尔文山脉重建家园。他们决定在山脉之中建造一座瞭望塔和一个魔法浮空岛,以便于在瞭望塔和浮空岛上可以俯视艾尔文山脉的全貌。
艾尔文山脉被描述为一个折线,给定每个点的坐标(横纵坐标均不小于0),按照横坐标从小到大顺次连接起来就是就是山脉的折线。折线上所有点的横坐标均不相同。如果一个位置与山脉任何一点的连线均不被挡住(但可以与地面相切),那么就说这一点可以望到整个艾尔文山脉。瞭望塔的塔身不会挡住视线,而且瞭望塔和浮空岛可以建造在同一位置。为节省建筑材料,瞭望塔塔身的高度必须尽量小,即从塔顶到塔底的距离尽量小,瞭望塔可以建在山坡上。由于气候因素,浮空岛应建立在海拔尽量低的位置(甚至可以建在地面上),海平面高度为0。如果有多个位置均满足条件,则选择横坐标最小的那个。瞭望塔和浮空岛横坐标范围应在艾尔文山脉横坐标范围之内。给定艾尔文山脉,请你求出瞭望塔和浮空岛的位置。
Input
第1行,一个整数N,表示描述艾尔文山脉的折线的顶点数。
第2-N+1行,每行两个整数,xi,yi表示折线上点的坐标。
Output
第1行,两个保留3位小数的浮点数x1,y1,表示瞭望塔顶端的坐标。
第2行,两个保留3位小数的浮点数x2,y2,表示浮空岛的坐标。
Sample Input
3
2 0
1 10
3 10
Sample Output
1.00 10.00
2.00 0.00
HINT
样例中描述的艾尔文山脉各个顶点,按照横坐标顺序顺次连接后的折线如下图所示:
瞭望塔应建造在山峰(8,6)处,塔顶端为(8,11),高度为5,此时瞭望塔的高度最小。
浮空岛建立在(9.54,9.85)处,海拔高度最低。
40%的数据2<=N<=10
100%的数据2<=N<=1 000 000;0<=xi,yi<=5 000 000
Source
版权所有者: 郭家宝
从2017年2月一直做到2018年1月才A掉了这道题……
跨越了将近一整年……
当时咱为什么这么菜QAQ
现在也很菜就是了QAQ
思路:
首先,求出所有山脉的半平面交。
然后,枚举半平面交上半平面的交点和最外圈两条线与边界的交点(可能不存在),找出浮空岛位置。
接着,构造函数
f(x)
表示坐标
x
处山脉的高度,
可以发现
h(x)
的极小值仅出现在半平面交点和山脉顶点上,枚举即可找出
minh(x)
,从而找出瞭望塔位置。
然后做完了…….
另外解析几何不知道比计算几何好写到哪里去了。
#include<bits/stdc++.h>
using namespace std;
inline int read()
{
int x=0;char ch=getchar();
while(ch<'0' || '9'<ch)ch=getchar();
while('0'<=ch && ch<='9')x=x*10+(ch^48),ch=getchar();
return x;
}
typedef double db;
typedef pair<db,db> point;
#define x first
#define y second
const int N=1000009;
const db eps=1e-8;
int n;
point p[N<<1];
int stk[N],top;
struct line
{
db k,b;
line(){};
line(point aa,point bb)
{
k=(aa.y-bb.y)/(aa.x-bb.x);
b=aa.y-k*aa.x;
}
bool operator < (line o)const
{
if(fabs(k-o.k)<eps)
return b<o.b;
return k<o.k;
}
}l[N];
inline point cross(const line &a,const line &b)
{
db x=(a.b-b.b)/(b.k-a.k);
return point(x,a.k*x+a.b);
}
inline void hpi(int &m)
{
for(int i=2;i<=n;i++)
l[++m]=line(p[i-1],p[i]);
sort(l+1,l+m+1);
top=0;
for(int i=1;i<=m;i++)
if(i==m || fabs(l[i].k-l[i+1].k)>eps)
{
while(top>=2)
{
point pc=cross(l[stk[top]],l[i]);
point pt=cross(l[stk[top]],l[stk[top-1]]);
if(pc.x-pt.x<eps)top--;
else break;
}
stk[++top]=i;
}
for(int i=1;i<=top;i++)
l[i]=l[stk[i]];
m=top;
}
inline bool chkmin(db &a,db b){if(a-b>eps){a=b;return 1;}return 0;}
inline db h(point a,line b,db c)
{
return max(0.0,((b.k*a.x+b.b)-a.y)*c);
}
inline point getpt(db x,line a)
{
return point(x,a.k*x+a.b);
}
inline db pos(db x,line a)
{
return a.k*x+a.b;
}
inline bool in(point x)
{
return p[1].x-x.x<eps && x.x-p[n].x<eps;
}
int main()
{
n=read();
for(int i=1;i<=n;i++)
p[i].x=read(),p[i].y=read();
sort(p+1,p+n+1);
int m;
hpi(m);
point ans2=point(1e18f,1e18f),cr;
for(int i=1,fl1=0,fl2=0;i<m;i++)
{
if(p[1].x<cross(l[i],l[i+1]).x && !fl1)
{
cr=getpt(p[1].x,l[i]);
if(ans2.y-cr.y>eps)
ans2=cr;
fl1=1;
}
if(p[n].x<cross(l[i],l[i+1]).x && !fl2)
{
cr=getpt(p[n].x,l[i]);
if(ans2.y-cr.y>eps)
ans2=cr;
fl2=1;
}
}
if(m==1)
{
cr=getpt(p[1].x,l[1]);
if(ans2.y-cr.y>eps)
ans2=cr;
cr=getpt(p[n].x,l[1]);
if(ans2.y-cr.y>eps)
ans2=cr;
}
for(int i=1;i<m;i++)
{
cr=cross(l[i],l[i+1]);
if(in(cr) && ans2.y-cr.y>eps)
ans2=cr;
}
point ans1=point(1e18f,1e18f);
db mv=1e18f;
int pi,pj;
for(pi=1,pj=1;pi<=n && pj<m;)
{
point pti=p[pi];
point ptj=cross(l[pj],l[pj+1]);
if(pti.x<ptj.x)
{
if(chkmin(mv,h(pti,l[pj],1)))
ans1=getpt(pti.x,l[pj]);
pi++;
}
else
{
if(1<pi && chkmin(mv,h(ptj,line(p[pi-1],p[pi]),-1)))
ans1=ptj;
pj++;
}
}
while(pi<=n)
{
point pti=p[pi++];
if(chkmin(mv,h(pti,l[pj],1)))
ans1=getpt(pti.x,l[pj]);
}
printf("%.2f %.2f\n",ans1.x,ans1.y);
printf("%.2f %.2f\n",ans2.x,ans2.y);
return 0;
}