題目大意:
就是一個人來到充滿摩天大樓的城市,所有大樓沒有寬度。建一直角座標系,給出每個建築的高度,現在求人站在(x,0)處能夠看到天空的範圍。(即不被摩天大樓阻擋)。答案只需要給出視角大小。
還是想了一會纔想出。.對於一個人能看到的左界,我們可以得到下式:
用求出左界的斜率即可反解該範圍的視角的角。而求最小即用單調棧維護斜率單減即可。右界同理可得。
#include<iostream>
#include<cstdio>
#include<vector>
#include<queue>
#include<stack>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn=1000100,inf=1e9;
const double pi=3.1415926535;
int t,n,cnt;
stack <pair<int,double> > s;//第一個是當前元素編號,第二個是與上一個元素的斜率。
struct node{
int x,h,bian;
double l,r;
}a[maxn];
bool cmp(node p,node q){
return p.x<q.x;
}
bool cmp2(node p,node q){
return p.bian<q.bian;
}
double getk(int x,int y){
double x1=a[x].x,x2=a[y].x,y1=a[x].h,y2=a[y].h;
return (y2-y1)/(x2-x1);
}
int main(){
int i,j,q;double k,a1,a2;
scanf("%d",&t);
for(j=1;j<=t;j++){
scanf("%d",&n);
printf("Case #%d\n",j);
cnt=0;
for(i=1;i<=n;i++){
scanf("%d%d",&a[i].x,&a[i].h);
a[i].bian=inf;//大樓的編號爲正無窮
}
cnt=n;
scanf("%d",&q);
for(i=1;i<=q;i++){
cnt++;
scanf("%d",&a[cnt].x);
a[cnt].bian=i;
a[cnt].h=0;
}
sort(a+1,a+cnt+1,cmp);//把人和摩天大樓同時排序,不用分類討論。
while(!s.empty())s.pop();
for(i=1;i<=cnt;i++){//求左界,維護斜率單減
while(!s.empty()){
k=getk(s.top().first,i);
if(k>=s.top().second)s.pop();
else break;
}
if(s.empty()){
a[i].l=0;
s.push(make_pair(i,double(inf)));
}
else {
a[i].l=getk(s.top().first,i);
s.push(make_pair(i,a[i].l));
}
}
while(!s.empty())s.pop();
for(i=cnt;i>=1;i--){//求右界,維護斜率單增
while(!s.empty()){
k=getk(s.top().first,i);
if(k<=s.top().second)s.pop();
else break;
}
if(s.empty()){
a[i].r=0;
s.push(make_pair(i,double(-inf)));
}
else {
a[i].r=getk(s.top().first,i);
s.push(make_pair(i,a[i].r));
}
}
sort(a+1,a+cnt+1,cmp2);
for(i=1;i<=q;i++){
a1=atan(abs(a[i].l));
a2=atan(abs(a[i].r));
printf("%.10lf\n",(pi-a1-a2)/pi*180);
}
}
return 0;
}