【BZOJ 2646】【NEERC 2011】flight

http://www.lydsy.com/JudgeOnline/problem.php?id=2646
夏令营alpq654321讲课时说这道题很简单但并没有几个人提交,最近想复习一下线段树,脑袋一热就开始写这道题。。。
询问\([i,j]\)内的抛物线在\([l,r]\)上的最大值,最大值只会出现在抛物线的极值处和\(l\)\(r\)处。
对于出现在极值处的最大值我用线段树套平衡树解决(看claris大佬用KDTree做的orzorzorz)
对于出现在\(l\)\(r\)处的最大值,对抛物线的编号建立线段树,线段树每个节点表示这个编号区间内的抛物线的上轮廓线,然后二分一下就可以了。
初始化时不断地归并两个孩子的上轮廓线作为自己的上轮廓线。
求上轮廓线时先找出两个上轮廓线所有的断点,再在两个断点之间讨论两个上轮廓线的交点。
一开始我讨论交点直接用一元二次方程求根公式,但这样会出现非常大的精度问题!(有几个情况精度很严重,比如一个抛物线与另一个抛物线相切)
看了claris大佬的代码,先求第一个交点,讨论一下,再求第二个交点再讨论一下,这样可以避免上述情况orzorzorz
这道题我写挂了好几次,也重写了好几次,出现了好几个错误:

  1. 建树套树时依次插入很费时,最好直接递归建树qwq我好蠢啊
  2. 归并两个上轮廓线时出现的精度问题qwq
  3. 求二次函数时出现的精度问题qwq
  4. lower_bound慢?手写二分。。。动态开点慢?手写内存池。。。
  5. 加了inline本地AC提交RE???还是不加inline吧。。。

时间复杂度\(O(n\log^2n+mlog^2n)\)
空间复杂度是\(O(n\log n)\)的!
总的来说本题需要卡精度+卡常(只要树套树不依次插入貌似就不会T了?)

#include<cmath>
#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
using namespace std;

inline double max(const double &a, const double &b) {return a > b ? a : b;}

const int N = 50003;
const double eps = 1e-8;

int n, m;

struct nodeL {
    double a, b, c; int p, x, y, pr;
    inline double get_key(double X) {
        return a * X * X + b * X + c;
    }
} L[N];

namespace Splay {
    struct node *null;
    struct node {
        node *ch[2], *fa;
        int pos, num, ma;
        int pl() {return fa->ch[1] == this;}
        void setc(node *r, int c) {ch[c] = r; if (r != null) r->fa = this;}
        void count() {ma = max(num, max(ch[0]->ma, ch[1]->ma));}
    } *root[N << 2], *tt, pool[N * 30];
    int rtn, top = 0;
    
    node *newnode(node *f, int nu1, int nu2, int nu3) {
        node *t = pool + top; ++top;
        t->ch[0] = t->ch[1] = null;
        t->fa = f;
        t->pos = nu1; t->num = nu2; t->ma = nu3;
        return t;
    }
    
    void rotate(node *r) {
        node *f = r->fa;
        int c = r->pl();
        if (f == root[rtn]) r->fa = null, root[rtn] = r;
        else f->fa->setc(r, f->pl());
        f->setc(r->ch[c ^ 1], c);
        r->setc(f, c ^ 1);
        f->count();
    }
    void splay(node *r, node *tar = null) {
        for (; r->fa != tar; rotate(r))
            if (r->fa->fa != tar) rotate(r->pl() == r->fa->pl() ? r->fa : r);
        r->count();
    }
    
    int fornow;
    
    int le(node *r, double tmp) {
        if (r == null) return -0x7fffffff;
        if (r->pos >= tmp) return le(r->ch[0], tmp);
        else if (r->pos > (fornow = le(r->ch[1], tmp))) {tt = r; return r->pos;}
        else return fornow;
    }
    
    int ri(node *r, double tmp) {
        if (r == null) return 0x7fffffff;
        if (r->pos <= tmp) return ri(r->ch[1], tmp);
        else if (r->pos < (fornow = ri(r->ch[0], tmp))) {tt = r; return r->pos;}
        else return fornow;
    }
        
    int get_max(double l, double r) {
        node *tl, *tr;
        le(root[rtn], l); tl = tt;
        ri(root[rtn], r); tr = tt;
        splay(tl); splay(tr, root[rtn]);
        return root[rtn]->ch[1]->ch[0]->ma;
    }
        
    void initnull() {null = newnode(null, 0, 0, 0); null->fa = null->ch[0] = null->ch[1] = null;}
    
    int id[N], cnt;
    bool cmp(int x, int y) {return L[x].x == L[y].x ? L[x].y < L[y].y : L[x].x < L[y].x;}
    
    node *BuildTree(int l, int r, node *f) {
        if (l > r) return null;
        int mid = (l + r) >> 1;
        node *t = newnode(f, L[id[mid]].x, L[id[mid]].y, L[id[mid]].y);
        if (mid == 0) t->pos = -1, t->num = t->ma = 0;
        if (mid == cnt + 1) t->pos = N, t->num = t->ma = 0;
        t->ch[0] = BuildTree(l, mid - 1, t);
        t->ch[1] = BuildTree(mid + 1, r, t);
        t->count();
        return t;
    }
    
    void init(int rt_num, int l, int r) {
        cnt = 0;
        for (int i = l; i <= r; ++i) id[++cnt] = i;
        stable_sort(id + 1, id + cnt + 1, cmp);
        root[rt_num] = BuildTree(0, cnt + 1, null);
        if (l == r) return;
        int mid = (l + r) >> 1;
        init(rt_num << 1, l, mid);
        init(rt_num << 1 | 1, mid + 1, r);
    }
    
    int query(int rt_num, int l, int r, int L, int R, double keyl, double keyr) {
        if (L <= l && r <= R) {
            rtn = rt_num;
            return get_max(keyl, keyr);
        }
        int mid = (l + r) >> 1, ans = 0;
        if (L <= mid) ans = max(ans, query(rt_num << 1, l, mid, L, R, keyl, keyr));
        if (R > mid) ans = max(ans, query(rt_num << 1 | 1, mid + 1, r, L, R, keyl, keyr));
        return ans;
    }
}

namespace SegmentTree {
    struct node {
        int id; double l, r;
        node (int _id = 0, double _l = 0, double _r = 0) : id(_id), l(_l), r(_r) {}
        double get(double X) {return L[id].a * X * X + L[id].b * X + L[id].c;}
        bool operator < (const node &A) const {
            return r < A.r;
        }
    };
    vector <node> T[N << 2], c;
    vector <double> q;
    
    double pos, tl, tr;
    int id[N], cnt = 0, tot;
    
    double cal(int x, int y, double l, double r) {
        double a = L[x].a - L[y].a, b = L[x].b - L[y].b, c = L[x].c - L[y].c;
        double delta = b * b - 4 * a * c, po;
        if (1.0 * (L[x].x - L[x].p) * L[y].y == 1.0 * L[x].y * (L[y].x - L[y].p)) {
            if (fabs(b) < eps) return r;
            else if ((po = -c / b) > l + eps && po < r) return po;
            else return r;
        }
        if (delta < -eps) return r;
        delta = sqrt(delta);
        tl = (-b + delta) / 2 / a;
        tr = (-b - delta) / 2 / a;
        if (tl < l + eps || tl > r - eps) tl = -1;
        if (tr < l + eps || tr > r - eps) tr = -1;
        po = r;
        if (tl != -1) po = min(po, tl);
        if (tr != -1) po = min(po, tr);
        return po;
    }
    
    void merge(vector <node> &A, vector <node> &B, vector <node> &C) {
        q.clear(); c.clear();
        double l, r, mid;
        int lena = A.size(), lenb = B.size(), tmpa = 0, tmpb = 0, tot = 0;
        q.push_back(-N);
        while (tmpa < lena && tmpb < lenb) {
            if (A[tmpa].r < B[tmpb].r) {
                if (A[tmpa].r > q[tot] + eps) q.push_back(A[tmpa].r), ++tot;
                ++tmpa;
            } else {
                if (B[tmpb].r > q[tot] + eps) q.push_back(B[tmpb].r), ++tot;
                ++tmpb;
            }
        }
        
        while (tmpa < lena) {if (A[tmpa].r > q[tot] + eps) q.push_back(A[tmpa].r), ++tot; ++tmpa;}
        while (tmpb < lenb) {if (B[tmpb].r > q[tot] + eps) q.push_back(B[tmpb].r), ++tot; ++tmpb;}
        tmpa = tmpb = 0;
        for (int i = 0; i < tot; ++i) {
            l = q[i]; r = q[i + 1];
            while (tmpa < lena && A[tmpa].r + eps < r) ++tmpa;
            while (tmpb < lenb && B[tmpb].r + eps < r) ++tmpb;
            if (tmpa == lena || A[tmpa].l > l + eps) {c.push_back(node(B[tmpb].id, l, r)); continue;}
            if (tmpb == lenb || B[tmpb].l > l + eps) {c.push_back(node(A[tmpa].id, l, r)); continue;}
            if (A[tmpa].id == 0 || B[tmpb].id == 0) {c.push_back(node(A[tmpa].id + B[tmpb].id, l, r)); continue;}
            
            while (r - l > eps) {
                pos = cal(A[tmpa].id, B[tmpb].id, l, r);
                mid = (l + pos) / 2;
                if (A[tmpa].get(mid) > B[tmpb].get(mid))
                    c.push_back(node(A[tmpa].id, l, pos));
                else
                    c.push_back(node(B[tmpb].id, l, pos));
                l = pos;
            }
        }
        
        int tt = c.size();
        for (int i, j, k = 0; k < tt; k = j) {
            for (i = k, j = k + 1; j < tt && c[i].id == c[j].id; ++j);
            C.push_back(node(c[i].id, c[i].l, c[j - 1].r));
        }
    }
    
    void BuildTree(int rt, int l, int r) {
        if (l == r) {
            T[rt].push_back(node(0, -N, L[l].p));
            T[rt].push_back(node(l, L[l].p, L[l].pr));
            T[rt].push_back(node(0, L[l].pr, N));
            return;
        }
        int mid = (l + r) >> 1;
        BuildTree(rt << 1, l, mid);
        BuildTree(rt << 1 | 1, mid + 1, r);
        
        merge(T[rt << 1], T[rt << 1 | 1], T[rt]);
    }
    
    int left, right, mid;
    double query(int rt, int l, int r, int L, int R, double q1, double q2) {
        if (L <= l && r <= R) {
            double ans = 0;
            left = 0; right = T[rt].size();
            while (left < right) {
                mid = (left + right) >> 1;
                if (T[rt][mid].r + eps < q1) left = mid + 1;
                else right = mid;
            }
            if (left != T[rt].size() && T[rt][left].l <= q1)
                ans = max(ans, T[rt][left].get(q1));
            left = 0; right = T[rt].size();
            while (left < right) {
                mid = (left + right) >> 1;
                if (T[rt][mid].r + eps < q2) left = mid + 1;
                else right = mid;
            }
            if (left != T[rt].size() && T[rt][left].l <= q2)
                ans = max(ans, T[rt][left].get(q2));
            return ans;
        }
        
        double ans = 0;
        int mid = (l + r) >> 1;
        if (L <= mid) ans = max(ans, query(rt << 1, l, mid, L, R, q1, q2));
        if (R > mid) ans = max(ans, query(rt << 1 | 1, mid + 1, r, L, R, q1, q2));
        return ans;
    }
}

int main() {
    scanf("%d", &n);
    double p, x, y;
    Splay::initnull();
    for (int i = 1; i <= n; ++i) {
        scanf("%lf%lf%lf", &p, &x, &y);
        L[i].p = p; L[i].x = x; L[i].y = y; L[i].pr = 2 * x - p;
        L[i].a = -y / ((x - p) * (x - p));
        L[i].b = -2 * x * L[i].a;
        L[i].c = y + L[i].a * x * x;
    }
    Splay::init(1, 1, n);
    SegmentTree::BuildTree(1, 1, n);
    
    scanf("%d", &m);
    int idl, idr;
    double ans, l, r;
    for (int i = 1; i <= m; ++i) {
        scanf("%d%d%lf%lf", &idl, &idr, &l, &r);
        ans = Splay::query(1, 1, n, idl, idr, l, r);
        ans = max(ans, SegmentTree::query(1, 1, n, idl, idr, l, r));
        printf("%.5lf\n", ans);
    }
    
    return 0;
}

转载于:https://www.cnblogs.com/abclzr/p/6622008.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值