牛客网暑期ACM多校训练营(第二场)C 二分 + 凸包

题目链接


题意:
给定 n 条斜率不为0的直线的参数 a , b y = a x + b ),有 m 次询问,每次给出一条直线的参数 c , d ( y = c x + d ),该直线与这 n 条直线的交点中,横坐标最大为多少?


思路:

对于两条直线 y = a x + b y = c x + d ,其交点的横坐标为:

x = b d a c

等价为两个点 ( a , b ) ( c , d ) 斜率的相反数。

故该问题可转化为:求出给定一点与平面 n 个点的斜率最小值。

此时假设我们正在考虑的询问点为 ( c , d ) ,则以直线 x = c ,将平面上的 n 个点分成两部分,而与询问点组成斜率最小的点,在左半区一定在尽量上面的地方,而右半区则尽量在下面的地方,且一定在最外围,即这 n 个点的凸包顶点上。
此时容易发现,我们只需要在左半区维护一个上凸壳,在右半区维护一个下凸壳,对凸壳上的点进行二分即可得到答案。

此题得解。


代码:

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long ll;
typedef long double ld;

const double eps = 1e-8;
const int A = 5e4 + 10;
class P{
public:
    ll x,y;
    int id;
    P(ll _x = 0, ll _y = 0, int _id = 0) {
        x = _x; y = _y; id = _id;
    }
    bool operator < (const P & rhs) const {
        return x < rhs.x;
    }
    P operator - (const P& rhs) const {
        return P(x - rhs.x, y - rhs.y);
    }
    ll operator * (const P& rhs) const {
        return x * rhs.y - y * rhs.x;
    }
    ld operator / (const P& rhs) const {
        if(x == rhs.x) return 0.0;
        return ld(y - rhs.y) / (x - rhs.x);
    }
}a[A], b[A], Sta[A];
int n, m, tot, j;
ld Ans[A];

ld Bi_search(P &x){
    int l = 1, r = tot;
    while (l < r) {
        int mid = (l+r)>>1;
        if (Sta[mid]/x > Sta[mid+1]/x) l = mid + 1;
        else                           r = mid;
    }
    return Sta[l] / x;
}

void solve(){
    j = 1;tot = 0;
    for (int i = 1; i <= m; i++) {
        while (j <= n && a[j].x < b[i].x) {
            while (tot >= 2 && (Sta[tot] - Sta[tot-1]) * (a[j] - Sta[tot]) >= 0) tot--;
            Sta[++tot] = a[j];
            j++;
        }
        if(tot == 0) Ans[b[i].id] = 1;
        else         Ans[b[i].id] = Bi_search(b[i]);
    }
    j = n;tot = 0;
    for (int i = m; i >= 1; i--) {
        while (j && a[j].x > b[i].x) {
            while (tot >= 2 && (Sta[tot] - Sta[tot-1]) * (a[j] - Sta[tot]) >= 0) tot--;
            Sta[++tot] = a[j];
            j--;
        }
        if(tot) Ans[b[i].id] = min(Ans[b[i].id], Bi_search(b[i]));
    }
}

int main(){
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        scanf("%lld%lld", &a[i].x, &a[i].y);
        a[i].id = 0;
    }
    scanf("%d", &m);
    for (int i = 1; i <= m; i++) {
        scanf("%lld%lld", &b[i].x, &b[i].y);
        b[i].id = i;
    }
    sort(a + 1, a + 1 + n);
    sort(b + 1, b + 1 + m);
    solve();
    for (int i = 1; i <= m; i++) {
        if(Ans[i] <= 0) printf("%.15f\n",-double(Ans[i]));
        else            printf("No cross\n");
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/WuBaizhe/article/details/81189113
今日推荐