• @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。

  • 相关阅读:
    统计学习方法 学习笔记(八):k 近邻法
    统计学习方法 学习笔记(七):拉格朗日对偶性
    统计学习方法 学习笔记(六):序列最小最优化算法SMO
    统计学习方法 学习笔记(五):支持向量机(下)
    统计学习方法 学习笔记(四):支持向量机(中)
    统计学习方法 学习笔记(三):支持向量机(上)
    统计学习方法 学习笔记(二):感知机
    CUDA npp运动检测模块性能测试
    CUDA H624解码性能测试
    利用GPU改善程序性能的一点心得
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/10224588.html
Copyright © 2020-2023  润新知