• P4097 [HEOI2013]Segment 李超线段树


    $ color{#0066ff}{ 题目描述 }$

    要求在平面直角坐标系下维护两个操作:

    1. 在平面上加入一条线段。记第 i 条被插入的线段的标号为 i
    2. 给定一个数 k,询问与直线 x = k 相交的线段中,交点最靠上的线段的编号。

    有n家洗车店从左往右排成一排,每家店都有一个正整数价格p[i]。有m个人要来消费,第i个人会驶过第a[i]个开始一直到第b[i]个洗车店,且会选择这些店中最便宜的一个进行一次消费。但是如果这个最便宜的价格大于c[i],那么这个人就不洗车了。请给每家店指定一个价格,使得所有人花的钱的总和最大。

    (color{#0066ff}{输入格式})

    第一行一个整数 n,表示共 n 个操作

    接下来 n 行,每行第一个数为 0 或 1

    若该数为 0,则后面跟着一个正整数 k,表示询问与直线 x = ((k + lastans – 1)%39989+1)相交的线段中交点(包括在端点相交的情形)最靠上的线段的编号,其中%表示取余。若某条线段为直线的一部分,则视作直线与线段交于该线段 y 坐标最大处。若有多条线段符合要求,输出编号最小的线段的编号

    若该数为 1,则后面跟着四个正整数 x0, y0, x1, y1,表示插入一条两个端点为 ((x0+lastans-1)%39989+1,(y0+lastans-1)%(10^9)+1)和 ((x1+lastans-1)%39989+1,(y1+lastans-1)%(10^9)+1) 的线段

    其中 lastans 为上一次询问的答案。初始时 lastans=0

    (color{#0066ff}{输出格式})

    对于每个 0 操作,输出一行,包含一个正整数,表示交点最靠上的线段的编 号。若不存在与直线相交的线段,答案为 0

    (color{#0066ff}{输入样例})

    6 
    1 8 5 10 8 
    1 6 7 2 6 
    0 2 
    0 9 
    1 4 7 6 7 
    0 5
    

    (color{#0066ff}{输出样例})

    2 
    0 
    3
    

    (color{#0066ff}{数据范围与提示})

    对于 30%的数据,n ≤ 1000
    对于 100%的数据,1 ≤ n ≤ (10^5), 1 ≤ k, x0, x1 ≤ 39989, 1 ≤ y0 ≤ y1 ≤ (10^9)

    (color{#0066ff}{题解})

    考虑用线段树维护下标为x的最大的y

    那么考虑两条直线

    如果出现如图情况,显然BP和PD对答案其实就没有贡献了

    线段树实际上维护的就是一个折线

    但是对于一个区间,没办法记录一个直线,怎么办?

    反正查询也是单点查询,我们可以考虑记录使得当前区间的mid位置的值最优的直线

    然后看交点的位置,判断往哪个方向递归

    实际上并不用求交点,更新当前区间的时候,如果现在的比当前区间的线段更优,就交换一下,用它去更新子树

    这时候只要判断斜率就知道改往哪个方向递归了

    这样操作其实是一种类似于标记永久化的东东,于是单点查询的时候要沿途取最优!

    #include <bits/stdc++.h>
    #define LL long long
    #define mid ((l + r) >> 1)
    LL in() {
    	char ch; LL x = 0, f = 1;
    	while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
    	for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
    	return x * f;
    }
    const int maxn = 1e5 + 10;
    const int mod1 = 39989;
    const int mod2 = 1e9;
    const double eps = 1e-6;
    struct St {
    protected:
        struct node {
            int s1, s2, id; double t1, t2;  //线段两个端点,对应的y
            node *ch[2];    
            node(int s1 = 0, int s2 = 0, int id = 0, double t1 = 0, double t2 = 0): s1(s1), s2(s2), id(id), t1(t1), t2(t2) {
                ch[0] = ch[1] = NULL;
            } 
            double gety(int x) { return s1 == s2? t1 : t1 + (getk() * (x - s1)); } //把x带入获得y
            double getk() { return (double)(t2 - t1) / (double)(s2 - s1); }   //斜率
            double getmin() { return std::min(t1, t2); }   //区间最小y
            double getmax() { return std::max(t1, t2); }   //区间最大y
            void ltrn(int x) { t1 = gety(x); s1 = x; }     //有一个x = (this)x的直线截取当前线段,取右边
            void rtrn(int x) { t2 = gety(x); s2 = x; }     //同上,取左边,就是缩小线段到当前区间范围内
        }*root, pool[maxn], *tail;
        int n;
        bool check(node *a, node *b, int x) {  //看看把x带入a是否比b优
            double vala = a->gety(x), valb = b->gety(x);
            return fabs(vala - valb) <= eps? a->id < b->id : vala > valb;
        }
        void build(node *&o, int l, int r) {
        	o = new node(l, r);
            if(l == r) return;
            build(o->ch[0], l, mid);
            build(o->ch[1], mid + 1, r); 
        }
        node *query(node *o, int l, int r, int pos) {
            if(l == r) return o;
            node *ans;
            if(pos <= mid) ans = query(o->ch[0], l, mid, pos);
            else ans = query(o->ch[1], mid + 1, r, pos);
            return check(ans, o, pos)? ans : o;   //每次取最优
        }
        void lazy(node *o, int l, int r, node *k) {
            if(o->s1 > k->s1) k->ltrn(o->s1);   //缩小线段的x范围到当前区间
            if(o->s2 < k->s2) k->rtrn(o->s2);
            if(check(k, o, mid)) {
            	std::swap(o->s1, k->s1);  //更优则交换
            	std::swap(o->s2, k->s2);
            	std::swap(o->t1, k->t1);
            	std::swap(o->t2, k->t2);
            	std::swap(o->id, k->id);
            }
            if(o->getmin() >= k->getmax()) return;  //没有贡献
            if(o->getk() <= k->getk()) lazy(o->ch[1], mid + 1, r, k);
            else lazy(o->ch[0], l, mid, k);
        }
        void ins(node *o, int l, int r, node *k) {  
            if(k->s1 > r || k->s2 < l) return;
        	node *newk = new(tail++) node(); *newk = *k; //这里必须开一个新的点,要保证递归完k并没有改变
            if(o->s1 > newk->s1) newk->ltrn(o->s1);
            if(o->s2 < newk->s2) newk->rtrn(o->s2);
            if(l == newk->s1 && r == newk->s2) return lazy(o, l, r, newk);
            ins(o->ch[0], l, mid, newk);
            ins(o->ch[1], mid + 1, r, newk);
        }
    public:
        void init(int n) { this->n = n; root = NULL; build(root, 1, n); }
        void ins(int s1, int s2, int id, int t1, int t2) {
            tail = pool;
            node *o = new node(s1, s2, id, t1, t2);
            if(s1 == s2) o->t1 = o->t2 = o->getmax();
            ins(root, 1, n, o);
        }
        int query(int pos) { return query(root, 1, n, pos)->id; }
    }T;
    int main(){
        T.init(mod1);
        int ans = 0, cnt = 0, s1, s2, t1, t2;
        for(int Q = in(); Q --> 0;) {
            if (!in()) printf("%d
    ", ans = T.query((in() + ans - 1) % mod1 + 1));
            else{
                s1 = (in() + ans - 1) % mod1 + 1;
                t1 = (in() + ans - 1) % mod2 + 1;
                s2 = (in() + ans - 1) % mod1 + 1;
                t2 = (in() + ans - 1) % mod2 + 1;
                if(s1 > s2) std::swap(s1, s2), std::swap(t1, t2);
                T.ins(s1, s2, ++cnt, t1, t2);
            }
        }
    }
    
  • 相关阅读:
    Domain Logic approaches
    Load data local infile 实验报告
    eclipse导入Maven项目
    MYSQL数据库导入数据时出现乱码的解决办法
    mysql创建用户并分配权限
    CDI Features
    Java Design Patterr
    关联tomcat源代码
    Spring AOP Capabilities and Goals
    CDI FEATURES
  • 原文地址:https://www.cnblogs.com/olinr/p/10627249.html
Copyright © 2020-2023  润新知