首页 > 其他分享 >李超线段树

李超线段树

时间:2022-10-10 21:57:14浏览次数:77  
标签:int 线段 xian 李超 mid vis id

李超线段树

维护很多个线段/直线,查询和某条 $ x = k $ 的直线的交点的纵坐标最大值
线段树上每个节点维护的是,在中点 $ mid $ 处取最大值的线段,称它为这个区间的优势线段,但不一定是在整个区间取最大值的线段
使用标记永久化,查询的时候直接用路径上的节点信息更新
加入新线段的时候要讨论
若当前区间没有优势线段,那么就是它了,然后返回
若新线段在中点处的答案比原来的优势线段的答案大,两条线段换一下
分别考虑在左右端点两条线段的取值,然后画个图就知道交点只会在一边,所以只会递归一个区间
整体复杂度是 $ O(n \log_n^2) $
例题: 李超线段树模板题
代码
#include<bits/stdc++.h>
#define ll long long
#define re register
#define ull unsigned long long
using namespace std;
inline int read()
{
    int x = 0, f = 0; char c = getchar();
    while(c < '0') f |= (c == '-'), c = getchar();
    while(c >= '0') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
    return f ? -x : x;
}

const double eps = 1e-8;
const int N = 1e5 + 10;
int n;

struct line{ double k, b; } xian[N];

double calc(const line &a, const double &x)
{ return a.k * x + a.b; }

int check(const double &x, const double &y)
{
    if(x - y > eps) return 1;
    if(y - x > eps) return -1;
    return 0;
}

struct LCT
{
    #define ls(x) (x << 1)
    #define rs(x) (x << 1 | 1)

    int vis[N << 2];

    LCT(){ memset(vis, 0, sizeof(vis)); }

    // 这个也是初始化vis
    // void build(int k, int l, int r)
    // {
    //     vis[k] = 0;
    //     if(l == r) return ;

    //     int mid = (l + r) >> 1;

    //     build(ls(k), l, mid);
    //     build(rs(k), mid + 1, r);
    // }

    void update(int k, int l, int r, int id)
    {
        if(vis[k] == 0){ vis[k] = id; return ; }

        int mid = (l + r) >> 1;

        if(check(calc(xian[id], mid), calc(xian[vis[k]], mid)) == 1) swap(vis[k], id);

        int dl = check(calc(xian[id], l), calc(xian[vis[k]], l));
        int dr = check(calc(xian[id], r), calc(xian[vis[k]], r));

        if(dl == 1 || (dl == 0 && id < vis[k])) update(ls(k), l, mid, id);
        if(dr == 1 || (dr == 0 && id < vis[k])) update(rs(k), mid + 1, r, id);
    }

    void motify(int k, int l, int r, int L, int R, int id)
    {
        if(L <= l && r <= R){ update(k, l, r, id); return ; }
        
        int mid = (l + r) >> 1;

        if(L <= mid) motify(ls(k), l, mid, L, R, id);
        if(R > mid) motify(rs(k), mid + 1, r, L, R, id);
    }

    int query(int k, int l, int r, int pos)
    {
        if(l == r) return vis[k]; 

        int mid = (l + r) >> 1, ans = 0;

        if(pos <= mid) ans = query(ls(k), l, mid, pos);
        if(pos > mid) ans = query(rs(k), mid + 1, r, pos);

        int x = check(calc(xian[vis[k]], pos), calc(xian[ans], pos));

        if(x == 1) return vis[k];
        if(x == -1) return ans;
        else return min(vis[k], ans);
    }
}T;

int main()
{
    n = read();
    int last = 0, num = 0;
    xian[0].b = -0x7ffffffffffffff;

    for(re int i = 1; i <= n; ++i)
    {
        int opt = read();
        if(opt == 0)
        {
            ll p = read();
            p = (p + last - 1 + 39989) % 39989 + 1;

            last = T.query(1, 1, 39989, p);
            
            printf("%d\n", last);
        }
        else
        {   
            ++num;
            ll s1 = read(), s2 = read(), s3 = read(), s4 = read();
            s1 = (s1 + last - 1 + 39989) % 39989 + 1;
            s2 = (s2 + last - 1 + 1000000000) % 1000000000 + 1;
            s3 = (s3 + last - 1 + 39989) % 39989 + 1;
            s4 = (s4 + last - 1 + 1000000000) % 1000000000 + 1;

            if(s1 > s3) swap(s1, s3), swap(s2, s4);

            if(s1 == s3) xian[num].k = 0, xian[num].b = max(s2, s4);
            else xian[num].k = (double)(s4 - s2) / (double)(s3 - s1), xian[num].b = 1.0 * s4 - xian[num].k * s3;
            
            T.motify(1, 1, 39989, s1, s3, num);
        }
    }

    return 0;
}

标签:int,线段,xian,李超,mid,vis,id
From: https://www.cnblogs.com/wenqizhi1125/p/16777542.html

相关文章

  • HDU 1698 Just a Hook(线段树)
    题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=1698思路:updata()区间替换,query()区间求和先上3篇博客1:http://blog.sina.com.cn/s/blog_a2dce6b30101l8bi.html 2:ht......
  • P3919 【模板】可持久化线段树 1(可持久化数组)
    还是用主席树来做(因为提到不同的版本),这时候的主席树不是以权值为下标的,就是普通的线段树,维护范围1~n,i存的是a[]中的数。1#include<bits/stdc++.h>2usingnamespac......
  • 线段树
    线段树线段树是一个很优秀的树结构,较简单,功能多,可以维护复杂信息。可以动态开点,可以打懒标记。基本概念线段树是基于分治思想的二叉树。为了引入线段树,我们来看一个......
  • 2022.10.3线段树复习笔记(未完待续)
    线段树原理及存储:如图,1即为根节点,存储着[1,5]的整个区间和,‘1’为左边界,‘5’为右边界,所以此节点表示的是[1,5]这个区间。线段树的每个节点向下二分,左儿子的编号为此节......
  • 线段树精选题
    SP2713GSS4-CanyouanswerthesequeriesIV题目大意\(n\)个数,和在\(10^18\)范围内,两种操作:区间开方下取整,查询区间和。思路区间开方,其实也是区间修改,只是每个元......
  • P3834 【模板】可持久化线段树 2
    P3834主席树模板,求区间第k小。1#include<bits/stdc++.h>2usingnamespacestd;3#definelctr[i].ch[0]4#definerctr[i].ch[1]5#defineLctr[j].ch[0......
  • 动态开点线段树
    引入在普通的线段树中,我们一般要开\(4N\)的数组以避免越界。然而,在一些题目中,空间限制并不允许我们这样做。考虑如下问题:有一个长度为\(n\)的数组,初始全部为\(0\)......
  • 李超线段树 学习笔记
    Idea主要用于动态维护一个线段或直线集合,支持在平面直角坐标系中插入一条线段或者直线,以及查询某一横坐标上的最值。考虑在x轴上建立一棵线段树,每一个节点\([l,r]\)......
  • 探求|线段或棱上是否存在一个点
    ......
  • [模板]zkw线段树
    讲解在这里[还有一个](https://wenku.baidu.com/view/f27db60ee87101f69e319544.html)A.数列操作单点修改,区间查询code//正青春的年华,就是应该献给直指星辰的梦想啊!......