【2010集训队出题】大灾变

【2010集训队出题】大灾变

by AmanoKumiko

Description

  艾泽拉斯世界经历一场亘古未有的地震过后,大地和海洋被完全撕裂,旧大陆残缺不全。联盟和部落各种族的居民们被迫离开了世代居住的家园,来寻找新的生存空间。原本平坦的陆地上现在隆起了一座座山峰,暴风城的人类开始在艾尔文山脉重建家园。他们决定在山脉之中建造一座瞭望塔和一个魔法浮空岛,以便于在瞭望塔和浮空岛上可以俯视艾尔文山脉的全貌。
  艾尔文山脉被描述为一个折线,给定每个点的坐标(横纵坐标均不小于0),按照横坐标从小到大顺次连接起来就是就是山脉的折线。折线上所有点的横坐标均不相同。如果一个位置与山脉任何一点的连线均不被挡住(但可以与地面相切),那么就说这一点可以望到整个艾尔文山脉。瞭望塔的塔身不会挡住视线,而且瞭望塔和浮空岛可以建造在同一位置。为节省建筑材料,瞭望塔塔身的高度必须尽量小,即从塔顶到塔底的距离尽量小,瞭望塔可以建在山坡上。由于气候因素,浮空岛应建立在海拔尽量低的位置(甚至可以建在地面上),海平面高度为0。如果有多个位置均满足条件,则选择横坐标最小的那个。瞭望塔和浮空岛横坐标范围应在艾尔文山脉横坐标范围之内。
  给定艾尔文山脉,请你求出瞭望塔和浮空岛的位置。

Input

  第1行,一个整数N,表示描述艾尔文山脉的折线的顶点数。
  第2-N+1行,每行两个整数,xi,yi表示折线上点的坐标。

Output

  第1行,两个保留2位小数的浮点数x1,y1,表示瞭望塔顶端的坐标。
  第2行,两个保留2位小数的浮点数x2,y2,表示浮空岛的坐标。

Sample Input

6
2 2
6 1
8 6
10 3
16 5
20 2

Sample Output

8.00 11.00
9.54 9.85

Data Constraint

  40%的数据2<=N<=10
  100%的数据2<=N<=1 000 000;0<=xi,yi<=5 000 000

Solution

把折线看成直线并延长,发现可行区域是一个凸包

很明显,如果求出了这个区域,我们只要对所有的交点都考虑一遍就能求出答案了

考虑怎么求解它,首先按照斜率排序,然后从小到大加进队列,

如果前两条直线的交点的横坐标大于前一条直线和当前直线交点的横坐标,那么把前一条直线删掉

之后就没什么事了,可能要卡下精度

Code

#pragma GCC optimize("Ofast")
#include<bits/stdc++.h>
using namespace std;
#define F(i,a,b) for(int i=a;i<=b;i++)
#define Fd(i,a,b) for(int i=a;i>=b;i--)
#define N 1000010
#define Ld long double
#define LL long long
#define e 0.00001
#define inf 1000000000000

Ld ans1x,ans1y,ans1=inf,ans2x,ans2y=inf,c[N],a[N],b[N];
int n,q[N],top;
struct node{Ld X,Y,val;}p[N];
struct mode{int x,y;}r[N];

Ld Slope(int x,int y){return (b[y]-b[x])/(a[y]-a[x]);}

Ld Cy(node x){return -x.val*x.X+x.Y;}

Ld Cross(node x,node y){return (y.Y-x.Y-y.val*y.X+x.val*x.X)/(x.val-y.val);}

Ld Fx(node x,Ld num){return x.val*(num-x.X)+x.Y;}

bool cmp1(mode x,mode y){return x.x<y.x;}

bool cmp2(node x,node y){return x.val<y.val||abs(x.val-y.val)<=e&&Cy(x)<Cy(y);}

int main(){
	scanf("%d",&n);
	F(i,1,n){
		int x,y;
		scanf("%d%d",&x,&y);
		r[i]=(mode){x,y};
	}
	sort(r+1,r+n+1,cmp1);
	F(i,1,n)a[i]=r[i].x,b[i]=r[i].y;
	F(i,2,n)p[i-1]=(node){a[i-1],b[i-1],Slope(i-1,i)};
	sort(p+1,p+n,cmp2);
	F(i,1,n-1){
		int pos=i;
		while(abs(p[pos+1].val-p[pos].val)<=e&&pos+1<=n-1)pos++;
		while(top>1&&Cross(p[q[top-1]],p[q[top]])>=Cross(p[q[top]],p[pos]))top--;
		q[++top]=pos;
		i=pos;
	}
	if(top==1){
		if(p[q[1]].val<0)printf("%.2Lf %.2Lf\n%.2Lf %.2Lf",a[1],b[1],a[n],b[n]);
		else printf("%.2Lf %.2Lf\n%.2Lf %.2Lf",a[1],b[1],a[1],b[1]);
		return 0;
	}
	F(i,1,top-1){
		c[i]=Cross(p[q[i]],p[q[i+1]]);
		Ld A=Fx(p[q[i]],c[i]);
		if(A<ans2y)ans2x=c[i],ans2y=A;
	}
	F(i,2,top){
		Ld wh=c[i-1];
		if(wh<a[1]||wh>a[n])continue;
		int w=lower_bound(a+1,a+n+1,wh)-a;
		Ld A=Fx(p[q[i]],wh)-(Slope(w,w-1)*(wh-a[w])+b[w]);
		if(A<ans1)ans1x=wh,ans1y=Fx(p[q[i]],wh),ans1=A;
	}
	F(i,1,n){
		if(a[i]>c[top-1]){
			Ld A=Fx(p[q[top]],a[i]);
			if(A-b[i]+e<ans1||abs(A-b[i]-ans1)<=e&&a[i]<ans1x)ans1x=a[i],ans1y=A,ans1=A-b[i];
			if(A+e<ans2y||abs(A-ans2y)<=e&&a[i]<ans2x)ans2x=a[i],ans2y=A;
			continue;
		}
		int w=lower_bound(c+1,c+top,a[i])-c;
		if(w>=1&&w<=top-1){
			Ld A=Fx(p[q[w]],a[i]);
			if(A-b[i]+e<ans1||abs(A-b[i]-ans1)<=e&&a[i]<ans1x)ans1x=a[i],ans1y=A,ans1=A-b[i];
			if(A+e<ans2y||abs(A-ans2y)<=e&&a[i]<ans2x)ans2x=a[i],ans2y=A;
		}
	}
	printf("%.2Lf %.2Lf\n%.2Lf %.2Lf",ans1x,ans1y,ans2x,ans2y);
	return 0;
}
posted @ 2021-12-09 15:49  冰雾  阅读(41)  评论(0编辑  收藏  举报