zoukankan      html  css  js  c++  java
  • @hdu


    @desription@

    一条数轴上有 N 个高楼,给定每栋楼的坐标和高度,保证两两之间坐标不相等。
    多次询问。每次询问如果在点 (qi, 0) 进行观测,有多大的角度范围可以观测到天空。保证询问的坐标上没有高楼且左右都至少有一栋楼。

    input
    多组数据,第一行给出数据组数 T。
    接下来的每组数据,第一行先给出 N,表示高楼数量,1 <= N <= 10^5。
    接下来 N 行,每行两个数字 xi, hi,表示高楼的坐标与高度,1 <= xi, hi <= 10^7。
    接下来一行给出 Q,表示询问次数,1 <= Q <= 10^5。
    接下来 Q 行,每行给出 qi,表示询问的坐标为 (qi, 0)。

    output
    对于第 x 组数据,先输出 “Case #x:”。
    然后对于每一个询问,输出相应的角度范围。

    sample input
    3
    3
    1 2
    2 1
    5 1
    1
    4
    3
    1 3
    2 2
    5 1
    1
    4
    3
    1 4
    2 3
    5 1
    1
    4
    sample output
    Case #1:
    101.3099324740
    Case #2:
    90.0000000000
    Case #3:
    78.6900675260

    @solution@

    以 x = qi 这一条直线为分界,分为左右两部分统计角度范围。接下来我们只考虑右半部分,左半部分同理。
    假如 (qi, 0) 右边有一个楼 (xj, hj),则从 (qi, 0) 向右观测,能够仰望到楼顶的角度 (arctan(frac{h_j}{q_j-x_i}))。如果要仰望到天空,则角度必须要大于等于这个值。那我们就是要求上面的最大值。
    又因为 (arctan) 是单调的,我们即是要求解 (frac{h_j}{q_j-x_i}) 的最大值。

    考虑这个式子的几何意义:过点 (qi, 0) 与 (xj, hj) 的直线的斜率。
    根据几何直观,我们需要维护一个上凸包,才能求到斜率的最大值。
    又根据几何直观,当观测点左移时,使斜率取到最大值的高楼是单调的。

    所以我们就可以用单调队列维护凸包了。
    一开始把询问存下来,然后询问坐标和高楼坐标一起排个序,然后从左往右再从右往左做两次,凸包维护一下,查询一下即可。

    @accepted code@

    #include<cmath>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    const int MAXN = 100000;
    const double PI = acos(-1);
    struct node{
    	int type, n;
    	double x, h;
    	node(int _t=0, int _n=0, double _x=0, double _h=0):type(_t), n(_n), x(_x), h(_h){}
    }a[2*MAXN + 5];
    bool operator < (node a, node b) {
    	return a.x < b.x;
    }
    double slope(int p, int q) {
    	return (a[p].h - a[q].h) / (a[p].x - a[q].x);
    }
    double ans[MAXN + 5];
    int stk[MAXN + 5], top;
    void solve() {
    	int N, Q;
    	scanf("%d", &N);
    	for(int i=1;i<=N;i++) {
    		double x, h;
    		scanf("%lf%lf", &x, &h);
    		a[i] = node(0, 0, x, h);
    	}
    	scanf("%d", &Q);
    	for(int i=1;i<=Q;i++) {
    		double q;
    		scanf("%lf", &q);
    		a[i + N] = node(1, i, q, 0);
    	}
    	sort(a+1, a+N+Q+1); top = 0;
    	for(int i=1;i<=N+Q;i++) {
    		while( top > 1 && slope(stk[top-1], stk[top]) <= slope(stk[top], i) )
    			top--;
    		if( a[i].type )
    			ans[a[i].n] += atan(fabs(slope(stk[top], i)));
    		else stk[++top] = i;
    	}
    	top = 0;
    	for(int i=N+Q;i>=1;i--) {
    		while( top > 1 && slope(stk[top], stk[top-1]) >= slope(stk[top], i) )
    			top--;
    		if( a[i].type )
    			ans[a[i].n] += atan(fabs(slope(stk[top], i)));
    		else stk[++top] = i;
    	}
    	for(int i=1;i<=Q;i++) {
    		printf("%.10f
    ", 180 - ans[i]/PI*180);
    		ans[i] = 0;
    	}
    }
    int main() {
    	int T; scanf("%d", &T);
    	for(int i=1;i<=T;i++) {
    		printf("Case #%d:
    ", i);
    		solve();
    	}
    }
    

    @details@

    从左往右加入点时,横坐标是单增的。
    但是从右往左加入点时,横坐标是单减的。
    所以两个代码不能直接复制粘贴 qwq。

  • 相关阅读:
    FOR XML PATH应用之用一条SQL语句横向合并结果行
    配置SharePoint 2007 过程中引发Microsoft.SharePoint.Upgrade.SPUpgradeException 异常(未能创建配置数据库)的一个解决方法
    Android仿360手机卫士九宫图
    第一个 Android 程序
    图解SharePoint 2010 安装过程
    在Windows Server 2008 R2(64位)上安装SharePoint 2007出现的一个错误的解决方法
    用Hint取代Messagebox来提示错误信息
    近期阅读计划
    测试下外链图片
    淘宝DBA的技术要求
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/10224588.html
Copyright © 2011-2022 走看看