http://acm.hdu.edu.cn/showproblem.php?pid=5033
2014 ACM/ICPC Asia Regional Beijing Online B 1002
BuildingTime Limit: 10000/5000 MS (Java/Others) Memory Limit: 262144/262144 K (Java/Others) Problem Description Once upon a time Matt went to a small town. The town was so small and narrow that he can regard the town as a pivot. There were some skyscrapers in the town, each located at position xi with its height hi. All skyscrapers located in different place. The skyscrapers had no width, to make it simple. As the skyscrapers were so high, Matt could hardly see the sky.Given the position Matt was at, he wanted to know how large the angle range was where he could see the sky. Assume that Matt‘s height is 0. It‘s guaranteed that for each query, there is at least one building on both Matt‘s left and right, and no building locate at his position. Input The first line of the input contains an integer T, denoting the number of testcases. Then T test cases follow. Each test case begins with a number N(1<=N<=10^5), the number of buildings. In the following N lines, each line contains two numbers, xi(1<=xi<=10^7) and hi(1<=hi<=10^7). After that, there‘s a number Q(1<=Q<=10^5) for the number of queries. In the following Q lines, each line contains one number qi, which is the position Matt was at. Output For each test case, first output one line "Case #x:", where x is the case number (starting from 1). Then for each query, you should output the angle range Matt could see the sky in degrees. The relative error of the answer should be no more than 10^(-4). Sample Input 3 Sample Output Case #1: |
题意:
城市看做二维平面,建筑看做x轴上某个位置为端点的竖着的线段,(xi,hi)表示在x轴xi位置有个高为hi的建筑(线段)。有多次询问,每次问人在某个平地上(x,0)能看到天空的角度。
题解:
维护 相邻两建筑顶(xi,hi)的连线的斜率的绝对值上升 的单调栈。
先把建筑和queries的点全部弄到一起,按xi排个序。然后从左到右来一波得出在某个空地往左看看到最高的是哪个建筑,再反过来来一波。
先按从左到右的情况来说:
维护单调栈,栈里存的是之后的空地可能看到的建筑,容易知这是递减的单调栈。
再思考,如果:
则只用存两边的点,中间那3个肯定看不到了。
如果:
则都要存,因为往右走的时候走着走着,右边第二个就比右边第一个高了,走着走着右边第三个又比右边第二个高了……(这时pop掉栈顶
可见我们存的是相邻两建筑顶(xi,hi)的连线的斜率的绝对值上升 的单调栈。
每看到一个空地,把栈首的不够高的都pop到,只留下那个能看到的最高的,然后把这个建筑加入结果记录中。(记录从这个空地往左看看到的最高的是哪个建筑)
反过来再来一遍。
最后再对询问搞一搞,就完啦。
(我写的时候以为xi hi是整数,所以里面都是用整数搞的,后来看Clarify才发现是实数,改了一波)
代码:
1 //#pragma comment(linker, "/STACK:102400000,102400000") 2 #include<cstdio> 3 #include<cmath> 4 #include<iostream> 5 #include<cstring> 6 #include<algorithm> 7 #include<cmath> 8 #include<map> 9 #include<set> 10 #include<stack> 11 #include<queue> 12 using namespace std; 13 #define ll long long 14 #define usll unsigned ll 15 #define mz(array) memset(array, 0, sizeof(array)) 16 #define mf1(array) memset(array, -1, sizeof(array)) 17 #define minf(array) memset(array, 0x3f, sizeof(array)) 18 #define REP(i,n) for(i=0;i<(n);i++) 19 #define FOR(i,x,n) for(i=(x);i<=(n);i++) 20 #define RD(x) scanf("%d",&x) 21 #define RD2(x,y) scanf("%d%d",&x,&y) 22 #define RD3(x,y,z) scanf("%d%d%d",&x,&y,&z) 23 #define WN(x) printf("%d\n",x); 24 #define RE freopen("D.in","r",stdin) 25 #define WE freopen("huzhi.txt","w",stdout) 26 #define mp make_pair 27 #define pb push_back 28 const double pi=acos(-1.0); 29 const double eps=1e-10; 30 31 const int maxn=111111; 32 const int maxm=2*maxn; 33 34 struct Bd { 35 double x,h; 36 int q; 37 } a[maxm]; 38 int n,Q; 39 double q[maxn]; 40 41 bool cmp(Bd x,Bd y) { 42 return x.x<y.x; 43 44 } 45 46 int L[maxn],R[maxn];///对应q[] 47 48 int b[maxn];///a[b[]].h 降序单调队列,并且相邻斜率(Δh/Δx)依次更斜 49 int r; 50 51 void mypb(int x) { 52 while(r>0 && a[b[r-1]].h <= a[x].h)r--;///保持降序 53 ///要保持:a[x]与a[b[r-1]]的斜率(负数) 小于 a[b[r-1]]与a[b[r-2]]的 54 ///(a[x].h - a[b[r-1]].h)/(a[x].x - a[b[r-1]].x) < (a[b[r-1]].h - a[b[r-2]].h)/(a[b[r-1]].x - a[b[r-2]].x) 55 ///(a[x].h - a[b[r-1]].h)*(a[b[r-1]].x - a[b[r-2]].x)<(a[b[r-1]].h - a[b[r-2]].h)*(a[x].x - a[b[r-1]].x) 56 ///实数 57 while(r>1 && ((a[x].h - a[b[r-1]].h))*(fabs(a[b[r-1]].x - a[b[r-2]].x))>=((a[b[r-1]].h - a[b[r-2]].h))*(fabs(a[x].x - a[b[r-1]].x))) 58 r--;///保持斜率 59 b[r++]=x; 60 //printf("PUSH b[%d]=%d\t%d %d\tr=%d\n",r-1,x,a[x].x,a[x].h,r); 61 } 62 63 void farm() { 64 int i; 65 ///一共有n+Q个元素,在1~n+Q里 66 int m=n+Q; 67 sort(a+1,a+m+1,cmp); 68 a[0].x=0; 69 a[0].h=0; 70 r=1; 71 b[0]=0; 72 FOR(i,1,m) { 73 //printf("a[%d].x=%d , .h=%d\n",i,a[i].x,a[i].h); 74 if(a[i].h>0.0)mypb(i); 75 else { 76 ///从队尾,找到对于x最斜的那个 77 ///当 a[b[r-1]].h/(a[i].x - a[b[r-1]].x) <= a[b[r-2]].h/(a[i].x - a[b[r-2]].x)时pop 78 /// a[b[r-1]].h*(a[i].x - a[b[r-2]].x) <= a[b[r-2]].h*(a[i].x - a[b[r-1]].x) 79 ///实数 80 while(r>1 && (a[b[r-1]].h)*(fabs(a[i].x - a[b[r-2]].x)) <= (a[b[r-2]].h)*(fabs(a[i].x - a[b[r-1]].x))) { 81 //printf("POP %d %d\n",a[b[r-1]].x,a[b[r-1]].h); 82 r--; 83 } 84 //printf("!r=%d!\n",r); 85 L[a[i].q]=b[r-1]; 86 //printf("L[%d]=b[%d]=%d\tr=%d\n",-a[i].h,r-1,b[r-1],r); 87 } 88 } 89 90 ///右边开始 91 a[m+1].x=a[m].x+1; 92 a[m+1].h=0; 93 r=1; 94 b[0]=m+1; 95 for(i=m; i>=1; i--) { 96 //printf("a[%d].x=%d , .h=%d\n",i,a[i].x,a[i].h); 97 if(a[i].h>0)mypb(i); 98 else { 99 ///从队尾,找到对于x最斜的那个 100 ///当 a[b[r-1]].h/(a[i].x - a[b[r-1]].x) <= a[b[r-2]].h/(a[i].x - a[b[r-2]].x)时pop 101 /// a[b[r-1]].h*(a[i].x - a[b[r-2]].x) <= a[b[r-2]].h*(a[i].x - a[b[r-1]].x) 102 ///可能超int 103 while(r>1 && (a[b[r-1]].h)*(fabs(a[i].x - a[b[r-2]].x)) <= (a[b[r-2]].h)*(fabs(a[i].x - a[b[r-1]].x))) { 104 //printf("POP %d %d\n",a[b[r-1]].x,a[b[r-1]].h); 105 r--; 106 } 107 R[a[i].q]=b[r-1]; 108 //printf("L[%d]=%d\n",-a[i].h,b[r-1]); 109 } 110 } 111 } 112 113 double gank(int x) { 114 Bd lb=a[L[x]]; 115 Bd rb=a[R[x]]; 116 //printf("%d %d %d %d\n",lb.x,lb.h,rb.x,rb.h); 117 double lj=atan2((lb.h),(fabs(lb.x-q[x]))); 118 double rj=atan2((rb.h),(fabs(rb.x-q[x]))); 119 return (pi-lj-rj)/pi*180.0; 120 } 121 122 int main() { 123 int T,cas=1; 124 int i; 125 RD(T); 126 while(T--) { 127 RD(n); 128 FOR(i,1,n)scanf("%lf%lf",&a[i].x,&a[i].h); 129 RD(Q); 130 FOR(i,1,Q) { 131 scanf("%lf",&q[i]); 132 a[n+i].x=q[i]; 133 a[n+i].h=0.0; 134 a[n+i].q=i;///这是第i个请求 135 } 136 farm(); 137 printf("Case #%d:\n",cas++); 138 FOR(i,1,Q) { 139 printf("%.10lf\n",gank(i)); 140 } 141 } 142 return 0; 143 }