http://acm.split.hdu.edu.cn/showproblem.php?pid=5033
好题啊,不得不说是好题,尤其是听cys讲过后,简直茅塞顿开
题意:坐标轴上n个点上有高h的线段,q次查询,每次查询给一个坐标x,问在(x,0)点上看不到线段的视角范围是多少
离线求,讲查询点和线段点整合在一起排个序,求左边看不到线段的角,也就是和左边线段上端点的最大夹角,同方法求右边
求的方法:n^暴力明显不可行,会发现有很多点是不需要的,使用单调栈维护一个上凸包,因为只有上凸包上的点是之后随着x坐标的变大会影响结果的
对于线段点,判断和栈顶两个元素组成的是否为上凸包,不是则把最栈顶出栈,直到找到
对于查询点,如果和栈顶端点的夹角小于栈内第二个元素的夹角,则栈顶出栈,因为排过序之后的x坐标只会越来越大,因此当前夹角小,之后只会更小
反向再求一遍即可
#include<bits/stdc++.h>
#define eps 1e-9
#define PI 3.141592653589793
#define bs 1000000007
#define bsize 256
#define MEM(a) memset(a,0,sizeof(a))
typedef long long ll;
using namespace std;
struct node
{
double x,h;
int id,flog;
bool operator < (const node u)const
{
if(x==u.x)
return flog>u.flog;
return x<u.x;
}
}num[200005],sta[200005];
int n,flag;
double ans[200005];
const double pai=180/acos(-1);
double xielv(node u,node v)
{
return flag*(u.h-v.h)/(u.x-v.x);
}
void solve()
{
int i,j,tail=0;
for(i=0;i<n;i++)
{
while(tail>1&&xielv(sta[tail-2],num[i])>xielv(sta[tail-1],num[i]))
tail--;
if(num[i].flog)
{
while(tail>0&&sta[tail-1].h<=num[i].h)
tail--;
sta[tail++]=num[i];
}
else
{
node temp=sta[tail-1];
// cout<<temp.h<<" "<<temp.x<<" "<<num[i].h<<" "<<num[i].x<<endl;
// cout<<atan(flag*(temp.h-num[i].h)/(temp.x-num[i].x))<<endl;
ans[num[i].id]+=pai*atan(flag*(temp.h-num[i].h)/(temp.x-num[i].x));
}
}
return ;
}
int main()
{
int q,T,i,tca=1;
cin>>T;
while(T--)
{
cin>>n;
for(i=0;i<n;i++)
{
scanf("%lf %lf",&num[i].x,&num[i].h);
num[i].flog=1;
}
cin>>q;
for(i=n;i<n+q;i++)
{
scanf("%lf",&num[i].x);
num[i].h=0;
num[i].flog=0;
num[i].id=i-n;
ans[i-n]=0;
}
n+=q;
sort(num,num+n);
flag=-1;
solve();
reverse(num,num+n);
flag=1;
solve();
printf("Case #%d:\n",tca++);
for(i=0;i<q;i++)
printf("%.12lf\n",180-ans[i]);
}
}