将人看成高度为0的楼,与楼混在一起按照横坐标排序,用单调栈正着扫一遍,再反着扫一遍,分别求出竖直线两侧的高度
出栈的条件:
1.当前楼的高度比栈顶的楼高
2.为了维护凸包的性质,若栈顶和之前楼的斜率大于栈顶和当前楼的斜率(均为绝对值),出栈
3.如果当前楼是人,向前出栈直至找到栈顶楼与人夹角大于之前的楼与人的夹角
然后将竖直线两侧的夹角相加就是最后结果
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <stack>
#include <cmath>
#define MAX 100007
#define PI (acos(-1))
using namespace std;
typedef pair<int,int> PII;
stack<PII> stk;
struct Building
{
int h,x,id;
bool operator < ( const Building& b ) const
{
return x < b.x;
}
}b[MAX<<1];
int t,n,m;
double ans[MAX];
double slope ( int id1 , int id2 )
{
return fabs ( ( b[id1].h - b[id2].h ) * 1.0 / ( ( b[id1].x - b[id2].x ) *1.0) ) ;
}
bool check ( int i )
{
if ( stk.empty() ) return false;
if ( stk.top().first == -1 ) return false;
if ( slope ( stk.top().first , stk.top().second ) > slope ( stk.top().second , i ) )
return true;
return false;
}
void solve ( int s , int e , int d )
{
while ( !stk.empty() ) stk.pop ( );
stk.push ( make_pair ( -1 , s ) );
for ( int i = s+d ; i != e ; i+= d )
{
if ( b[i].h )
{
while ( !stk.empty() && b[stk.top().second].h <= b[i].h )
stk.pop ( );
while ( check ( i ) )
stk.pop ( );
if ( stk.empty() ) stk.push ( make_pair ( -1 , i ) );
else stk.push ( make_pair ( stk.top().second , i ) );
}
else
{
while ( stk.top().first != -1 &&slope ( stk.top().second , i ) < slope ( stk.top().first , i ) )
stk.pop ( );
ans[b[i].id] += atan ( 1.0/slope ( stk.top().second , i ) )/PI*180.0;
}
}
}
int main ( )
{
scanf ( "%d" , &t );
int c = 1;
while ( t-- )
{
scanf ( "%d" , &n );
for ( int i = 1 ; i <= n ; i++ )
{
scanf ( "%d%d" , &b[i].x , &b[i].h );
b[i].id = -1;
}
scanf ( "%d" , &m );
for ( int i = 1 ; i <= m ; i++ )
{
scanf ( "%d" , &b[i+n].x );
b[i+n].h = 0;
b[i+n].id = i;
}
sort ( b+1 , b+n+m+1 );
memset ( ans , 0 , sizeof ( ans ) );
solve ( 1 , n+m , 1 );
solve ( n+m , 1 , -1 );
printf ( "Case #%d:\n" , c++ );
for ( int i = 1 ; i <= m ; i++ )
printf( "%.10lf\n" , ans[i] );
}
}