用单调栈维护斜率,使之斜率单调递增,左右各跑一遍,具体的可以看代码里的注释
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cmath>
#include <iomanip>
#define ll long long
using namespace std;
const int N = 1e5+9;
const double pi = acos(-1.0);
struct Node
{
int x,h;
bool operator < (const Node &a)const {
return x<a.x;
}
}node[N<<2],s[N<<2];
double ans[N];
int n,q;
int check(Node &a,Node &b,Node c){
if(c.h<=0)
c.h=0;
return (ll)(a.h-c.h)*(c.x-b.x)>=(ll)(b.h-c.h)*(c.x-a.x);//比较斜率,斜率变小返回true
}
double getAngle(Node a,Node b)
{
return atan((double)(b.x-a.x)/(double)(a.h)); //b(人)的高度为0
}
void work() //维护斜率递增的单调栈
{
int head = 0;
for(int i = 0; i<n+q; i++){
if(node[i].h<=0){
while(head>=2&&check(s[head-2],s[head-1],node[i])) //斜率变小,出栈
head--; //(感觉像是多余的一句话等会去掉试试)划掉!这句很重要,不能去,
//即使是人也要满足斜率递增,建议画图看看。
ans[-node[i].h]+=getAngle(s[head-1],node[i]);
}
else{
while(head&&s[head-1].h<=node[i].h) //去掉凹进去的点,肯定不符合条件
head--;
while(head>=2&&check(s[head-2],s[head-1],node[i])) //斜率变小,出栈
head--;
s[head++] = node[i];
}
}
}
int main()
{
//freopen("in.text","r",stdin);
ios::sync_with_stdio(false);
int t;
cin>>t;
//cout<<t<<endl;
int cas=1;
while(t--)
{
cin>>n; //cout<<n<<endl;
for(int i = 0; i<n; i++) cin>>node[i].x>>node[i].h;
cin>>q; //cout<<q<<endl;
for(int i = 0; i<q; i++) {cin>>node[n+i].x;node[n+i].h= -i;}//记录人(询问)的位置
memset(ans,0,sizeof(ans));
sort(node,node+n+q);
// cout<<"#"<<endl;
// for(int i = 0; i<n+q; i++){
// printf("%d %d\n",node[i].x,node[i].h);
// } cout<<"#"<<endl;
work();
reverse(node,node+n+q); //变换顺序
for(int i = 0; i< n+q; i++)
node[i].x = -node[i].x+10000000; //变换坐标,相当于关于x轴对称,在往右平移
//感觉可以直接这样,再sort一下更好理解
work();
// cout<<"#"<<endl;
// for(int i = 0; i<n+q; i++){
// printf("%d %d\n",node[i].x,node[i].h);
// } cout<<"#"<<endl;
cout<<"Case #"<<cas<<":"<<endl;
cout.setf(ios::fixed);
for(int i = 0; i<q; i++)
//cout<<ans[i]*180.0/pi<<endl;
cout << setprecision(10) << ans[i]*180.0/pi << endl;
//printf("%.10f\n",ans[i]*180.0/pi);
cas++;
}
return 0;
}