18牛客暑假多校C(计算几何)

题目大意:

n ( ≤ 50000 ) n(\le 50000) n(50000)条直线( y = a x + b y=ax+b y=ax+b), m ( ≤ 50000 ) m(\le 50000) m(50000)个询问,每次询问给出一条直线: y = c x + d y=cx+d y=cx+d询问这条直线和 n n n条直线交点最远的点的横坐标(答案必须大于等于0)

解题思路:

  • 相交的点为如下方程:
    a x + b = c x + d x = b − d c − a → x = b − d − a − ( − c ) ax+b=cx+d \\ x=\frac{b-d}{c-a} \rightarrow x=\frac{b-d}{-a-(-c)} \\ ax+b=cx+dx=cabdx=a(c)bd

  • 问题就变成了有 n ( − a , b ) n(-a,b) n(a,b)个点,每次询问给出一个点 ( − c , d ) (-c,d) (c,d),求斜率最大值

  • 可以分成两段来看,先看 − c -c c左边的点,将这些点构成凸包,之后在凸包上二分即可,右边同理

AC代码:

#include <bits/stdc++.h>
#define ft first
#define sd second
#define pb push_back
#define IOS ios::sync_with_stdio(false), cin.tie(0), cout.tie(0) //不能跟puts混用
#define seteps(N) fixed << setprecision(N)
#define endl "\n"
const int maxn = 1e5 + 10;
using namespace std;
typedef long long ll;
typedef double db;
typedef pair<int, int> pii;
const ll mod = 1e9 + 7;
const db eps = 1e-8;
int sgn(db x) {
    if (fabs(x) < eps) return 0;
    else return x < 0 ? -1 : 1;
}
int n, m;
struct Point {
    db x, y;
    int id;
    db operator ^ (Point p) {return x * p.y - y * p.x;}
    Point operator - (Point p) {return {x - p.x, y - p.y};}
    bool operator < (Point p) {
        if (sgn(x - p.x)) return x < p.x;
        else return y < p.y;
    }
} p[maxn], t[maxn];
int cross(Point p1, Point p2, Point p3) {return sgn((p2 - p1) ^ (p3 - p1));}
int cnt = 0;
db ans[maxn];
void solve() {
    cnt = 0;
    for (int i = 1; i <= n + m; i++) {
        if (p[i].id) {
            if (!cnt) continue;
            int l = 1, r = cnt, m;
            while (l < r) {
                m = l + r >> 1;
                if (cross(t[m], t[m + 1], p[i]) > 0) l = m + 1; //利用叉积来找最大的哪个斜率
                //画下图可以知道,斜率最大的那个点,必然跟凸包相切,那么cross叉积就小于0,且上一个点叉积大于0
                else r = m;
            }
            ans[p[i].id] = max(ans[p[i].id], (p[i].y - t[l].y) / (p[i].x - t[l].x));
        }
        else {
            while (cnt > 1 && cross(t[cnt - 1], t[cnt], p[i]) <= 0) cnt--;
            t[++cnt] = p[i];
        }
    }
}
void print() {
    for (int i = n + 1; i <= n + m; i++)
        if (sgn(ans[i]) <= 0) cout << "No cross" << endl;
        else cout << seteps(10) << ans[i] << endl;
}
int main() {
    IOS;
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> p[i].x >> p[i].y, p[i].x = -p[i].x;
    cin >> m;
    for (int i = n + 1; i <= n + m; i++) cin >> p[i].x >> p[i].y, p[i].id = i, p[i].x = -p[i].x;
    sort (p + 1, p + n + m + 1);
    solve();
    reverse(p + 1, p + n + m + 1);//翻转
    solve();
    print();
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值