洛谷 - CF70D Professor‘s task 题解

动态凸包

分别用两个std::map:top 与 down 来维护上下凸包。需要支持:

  • 单点加入
  • 单点询问与凸包位置关系

其中 top[x]/down[x] 表示的是凸包上横坐标为 x 的点的纵坐标,即top[x]/down[x]=y

// 代码部分参考:https://www.luogu.com.cn/paste/lto905of

#include<bits/stdc++.h>
using namespace std;
const double cd = 0.0000001;

int q, opt, x, y;

struct str1 {
    double a, b;
    str1() {
        a = b = 0;
    }
    str1(double A, double B) {
        a = A;
        b = B;
    }
    double d(double x) {
        return a * x + b;
    }
};

struct str2 {
    str1 mx;
    int i1, i2;
    str2() {
        mx = str1(-1e9, -1e9);
        i1 = i2 = 0;
    }
};

struct str3 {
    int tot, root;
    str2 num[100005];
    void v(int& i7, double l, double r, str1 s, int dep) {
        if (l > r || !dep) {
            return;
        }
        if (!i7) {
            i7 = ++tot;
        }
        if (num[i7].mx.d((l + r) / 2.0) < s.d((l + r) / 2.0)) {
            swap(num[i7].mx, s);
        }
        if (num[i7].mx.d(l) < s.d(l)) {
            v(num[i7].i1, l, (l + r) / 2.0, s, dep - 1);
        }
        if (num[i7].mx.d(r) < s.d(r)) {
            v(num[i7].i2, (l + r) / 2.0, r, s, dep - 1);
        }
    }
    bool query(int rt, double l, double r, str1 ss, double ll, double rr) {
        double lll, rrr;
        if (ll > rr) {
            return 0;
        }
        if (!rt || (ll <= (l + r) / 2.0 && (l + r) / 2.0 <= rr && num[rt].mx.d((l + r) / 2.0) < ss.d((l + r) / 2.0))) {
            return 1;
        }
        lll = ll;
        rrr = rr;
        if (num[rt].mx.d(l) < ss.d(l) || num[rt].mx.d((l + r) / 2.0) < ss.d((l + r) / 2.0)) {
            rr = min(rr, (l + r) / 2.0);
            if (num[rt].mx.d(ll) > ss.d(ll)) {
                ll = max(ll, (num[rt].mx.b - ss.b) / (ss.a - num[rt].mx.a)) + cd;
            }
            if (num[rt].mx.d(rr) > ss.d(rr)) {
                rr = min(rr, (num[rt].mx.b - ss.b) / (ss.a - num[rt].mx.a)) - cd;
            }
            if (query(num[rt].i1, l, (l + r) / 2.0, ss, ll, rr)) {
                return 1;
            }
        }
        if (num[rt].mx.d(r) < ss.d(r) || num[rt].mx.d((l + r) / 2.0) < ss.d((l + r) / 2.0)) {
            ll = max(lll, (l + r) / 2.0);
            rr = rrr;
            if (num[rt].mx.d(ll) > ss.d(ll)) {
                ll = max(ll, (num[rt].mx.b - ss.b) / (ss.a - num[rt].mx.a)) + cd;
            }
            if (num[rt].mx.d(rr) > ss.d(rr)) {
                rr = min(rr, (num[rt].mx.b - ss.b) / (ss.a - num[rt].mx.a)) - cd;
            }
            if (query(num[rt].i2, (l + r) / 2.0, r, ss, ll, rr)) {
                return 1;
            }
        }
        return 0;
    }
} A[4];

int main() {
    cin >> q;
    for (int i = 1; i <= q; i++) {
        cin >> opt >> x >> y;
        if (opt == 1) {
            A[0].v(A[0].root, 0.0, 1.0, str1(1.0 * x + 1.0 * y, -1.0 * y), 15);
            A[1].v(A[1].root, 0.0, 1.0, str1(1.0 * x - 1.0 * y, 1.0 * y), 15);
            A[2].v(A[2].root, 0.0, 1.0, str1(-1.0 * x - 1.0 * y, 1.0 * y), 15);
            A[3].v(A[3].root, 0.0, 1.0, str1(1.0 * y - 1.0 * x, -1.0 * y), 15);
        }
        else {
            if (A[0].query(A[0].root, 0.0, 1.0, str1(1.0 * x + 1.0 * y, -1.0 * y), 0.0, 1.0)) {
                cout << "NO\n";
                continue;
            }
            if (A[1].query(A[1].root, 0.0, 1.0, str1(1.0 * x - 1.0 * y, 1.0 * y), 0.0, 1.0)) {
                cout << "NO\n";
                continue;
            }
            if (A[2].query(A[2].root, 0.0, 1.0, str1(-1.0 * x - 1.0 * y, 1.0 * y), 0.0, 1.0)) {
                cout << "NO\n";
                continue;
            }
            if (A[3].query(A[3].root, 0.0, 1.0, str1(1.0 * y - 1.0 * x, -1.0 * y), 0.0, 1.0)) {
                cout << "NO\n";
                continue;
            }
            cout << "YES\n";
        }
    }
    return 0;
}

代码短,跑得还快,真香~

自此,我们已经写学习了动态凸包所需的所有操作!

模板:CF70D. Professor's task

题解仅供学习参考使用

抄袭、复制题解,以达到刷 AC 率/AC 数量或其他目的的行为,在洛谷是严格禁止的。

洛谷非常重视学术诚信。此类行为将会导致您成为作弊者。 具体细则请查看

洛谷社区规则

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值