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

李超线段树

时间:2023-10-27 14:58:33浏览次数:39  
标签:int 线段 李超 mid 区间 x0 x1

P4097 【模板】李超线段树 / [HEOI2013] Segment

强制在线,那么这种问题该如何解决?

我们可以把任务转化为维护如下操作:

  • 加入一个一次函数
  • 给定 \(k\),求定义域包含 \(k\) 的所有一次函数中,在 \(k\) 处取值最大的那个,如果有多个函数取值相同,选编号最小的。

注意:有可能线段斜率不存在

看到区间修改,我们按照线段树解决区间问题的常见方法,给每个节点一个懒标记。标记 \(l_i\)表示用 \(l_i\) 修改这个区间。

现在我们需要插入一条线段 \(f_i\),考虑一个被 \(f_i\) 完整覆盖的线段树区间。若该区间没有标记,就直接标记,否则就递归下传标记。

设该区间原本线段为 \(g_i\),把该区间根据 \(f_i\)是否大于 \(g_i\) 分为两个子区间,其中肯定有一个子区间被左区间或右区间完全包含。

也就是说,在两条线段中,肯定有一条线段,只可能成为左区间的答案,或者只可能成为右区间的答案。我们用这条线段递归更新对应子树,用另一条线段作为懒标记更新整个区间,这就保证了递归下传的复杂度。当一条线段只可能成为左或右区间的答案时,才会被下传,所以不用担心漏掉某些线段。

具体来说,当前区间的终点为 \(mid\),如果 \(f(mid)>g(mid)\),交换 \(f\) 和 \(g\)。

当 \(f\) 中点劣于 \(g\) 时 :

  1. 在左端点时 \(f\) 优于 \(g\) 那么 \(f\) 和 \(g\) 的交点一定在左区间,\(f\) 只可能在左区间才能优于 \(g\),递归左区间。
  2. 在右端点时 \(f\) 优于 \(g\) 那么 \(f\) 和 \(g\) 的交点一定在右区间,\(f\) 只可能在右区间才能优于 \(g\),递归右区间。
  3. 若在左右区间都是 \(g\) 更优,那么不需要下传。

其实对每个区间都用一条最优的线段表示,如果你加入的线段劣于该线段
就判断在那个区间可以更优。

至于查询,可以到达的所有线段取最大值就行了。

int n,last;
struct line{
    ldb k,b;
}p[N];
int cnt;
inline void add(int x0, int y0, int x1, int y1) {
    cnt++;
    if(x0==x1)p[cnt].k=0,p[cnt].b=max(y0, y1);
    else p[cnt].k=1.0*(y1-y0)/(x1-x0),p[cnt].b=y0-p[cnt].k*x0;
}
int s[N];
inline ldb calc(int id,int d) { return p[id].b+p[id].k*d;}
inline int cmp(ldb x,ldb y) {
    if(x-y>eps)return 1;
    if(y-x>eps)return -1;
    return 0;
}
inline void change(int k,int l,int r,int u){
    int &v=s[k],mid=(l+r)>>1;
    int bmid=cmp(calc(u,mid),calc(v,mid));
    if(bmid==1||(!bmid&&u<v))swap(u,v);
    int bl=cmp(calc(u,l),calc(v,l)),br=cmp(calc(u,r),calc(v,r));
    if(bl==1||(!bl&&u<v))change(lc,l,mid,u);
    if(br==1||(!br&&u<v))change(rc,mid+1,r,u);
}
inline void insert(int k,int tl,int tr,int l,int r,int u){
    if(l<=tl&&tr<=r) {
        change(k,tl,tr,u);
        return;
    }
    int mid=(tl+tr)>>1;
    if(l<=mid)insert(lc,tl,mid,l,r,u);
    if(mid<r)insert(rc,mid+1,tr,l,r,u);
}
inline pdi pmax(pdi x,pdi y){
    if(cmp(x.fi,y.fi)==-1)return y;
    else if(cmp(x.fi,y.fi)==1)return x;
    return x.se<y.se?x:y;
}
inline pdi ask(int k,int l,int r,int d){
    if(l==r)return {calc(s[k],d),s[k]};
    int mid=(l+r)>>1;
    pdi res={calc(s[k],d),s[k]};
    if(mid>=d)res=pmax(res,ask(lc,l,mid,d));
    else res=pmax(res,ask(rc,mid+1,r,d));
    return res;
}
signed main(){
    n=read();
    int op,k,x0,y0,x1,y1;
    up(i,1,n){
        op=read();
        if(op==1){
            x0=(read()+last-1+mod1)%mod1+1;
            y0=(read()+last-1+mod2)%mod2+1;
            x1=(read()+last-1+mod1)%mod1+1; 
            y1=(read()+last-1+mod2)%mod2+1;
            if (x0>x1)swap(x0,x1),swap(y0,y1);
            add(x0,y0,x1,y1);
            insert(1,1,mod1,x0,x1,cnt);
        }
        else {
            k=read();
            k=(k+last-1+mod1)%mod1+1;
            last=ask(1,1,mod1,k).second;
            cout<<(last)<<endl;
        }
    }
    return 0;
}

标签:int,线段,李超,mid,区间,x0,x1
From: https://www.cnblogs.com/LiQXing/p/17792352.html

相关文章

  • 可持久化线段树学习笔记
    主席树的定义主席树,也称可持久化线段树,什么是可持久化线段树呢,即为一颗记录了所有更新过程的线段树。能够处理出从第$i$次更新到第$j$次更新的线段树变化。前置知识值域线段树值域线段树的区间存的并不是节点信息,而是在值在某一范围内的数的个数。如图就是一棵值域线段树......
  • P5537 【XR-3】系统设计 题解-哈希+线段树二分
    20231026P5537【XR-3】系统设计题解-哈希+线段树二分这个东西怎么会和哈希有关?!直接寄。Statement这个系统首先需要输入一棵\(n\)个点的有根树和一个长度为\(m\)的序列\(a\),接下来需要实现\(q\)个操作。操作分两种:1xlr表示设定起点为有根树的节点\(x\),接下来......
  • 可持久化线段树
    可持久化线段树LuoguP3834【模板】可持久化线段树2特点:支持查询线段树的历史版本实现:每次修改只涉及\(log_2N\)个点,所以每次修改就先复制上一个状态,然后修改涉及到的一条链即可,时间复杂度\(O(Nlog^2N)\)(修改因为要离散化,是双log;查询单log)注意要保留每个状态的root,左右儿子......
  • 算法笔记(1)线段树
    原发表于个人博客。前言线段树,是数据结构皇冠上的明珠(我编的)。它用途广泛,被一代代的oier应用,改进,优化。本文介绍了线段树的基础知识和各种拓展(包括权值线段树,可持久化线段树),各种优化方式(包括zkw线段树,动态开点,离散化),希望能帮到更多的oier。在学习线段树前,默认你应该学会一下......
  • 【学习笔记】线段树合并
    前置知识:动态开点权值线段树。线段树合并,顾名思义,就是将两棵权值线段树合并在一起。为什么不把两棵普通的线段树合并呢?因为那样好像没啥用。我们知道,权值线段树支持着查询某个数的个数、查询第\(k\)大/小的数等操作,有了合并操作之后就可能会支持一些令人意想不到的操作。放张......
  • P3373 【模板】线段树 2
    【模板】线段树2如题,已知一个数列,你需要进行下面三种操作:将某区间每一个数乘上\(x\);将某区间每一个数加上\(x\);求出某区间每一个数的和。输入格式第一行包含三个整数\(n,q,m\),分别表示该数列数字的个数、操作的总个数和模数。第二行包含\(n\)个用空格分隔的整数,其中第\(i\)......
  • T125847 【模板】动态开点线段树
    \(T125847\)题目背景注意:请注意时间限制是800ms请使用较快的输入输出注意:空间限制是128MB请不要开longlong时限在std的2.5倍以上题目描述有一个有\(1000000000\)个数的初始值全为\(0\)的区间,你要进行两种操作:将某区间每一个数加上 \(x\)求出某区间每一个数的和输入格式第一行包......
  • 算法学习笔记(31): 李超线段树
    李超线段树是一种按照值域维护一次函数最值的数据结构,其核心在于一次函数和值域的双单调性。如果预先对于值域离散也可以维护其最值。也就是说只要满足时一次函数,以及下标的单调性都可以利用李超线段树维护。李超线段树就是利用线段树来维护一次函数的最值,每一个结点对应了一......
  • 962. 最大宽度坡(权值线段树, 权值树状数组)
     本题要快速找到某个数字在数组中左边<=它的数的最小下标。可以建立一个权值线段树,nums[i]处维护最小下标。classSolution{public:conststaticintN=50010,INF=0x3f3f3f3f;structNode{intl,r,v;}tr[N*4];voidpushup(int......
  • 点到线段的距离
    情况1情况2情况3情况4 publicstaticfloatPointToLineSegmentDistance(Vector2P,Vector2A,Vector2B){floata=Vector2.Distance(A,B);floatb=Vector2.Distance(A,P);floatc=Vector2.Distance(B,P);if(b<=float.MinValue||......