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

李超线段树

时间:2024-07-29 21:32:03浏览次数:6  
标签:return val int 线段 李超 long using define

李超线段树

  • 用途 : 在平面上插入一个线段或直线,求出与\(x=a\)相交的线段中纵坐标最大的线段编号。

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

将任务转化成

  1. 插入一个定义域为\([l,r]\)的一次函数
  2. 给定\(k\),求定义域包含\(k\)的所有一次函数中,在\(x=k\)处取值最大的那个,如果有多个函数取值相同,选编号最小的。

注意 : 若线段垂直于\(x\)轴,设线段两端点为\((x,y_0),(x,y_1),y_0\le y_1\),则插入定义域为\([x,x]\)的一次函数\(f(x)=0*x+y_1\)

做法 : 维护每个区间的中点处最高的线段。

假设当前区间原最优线段为\(g\),新插入的线段为\(f\)

假设当前在中点处

  • 若\(f\)更优,则将\(f\)与\(g\)交换。
  • 若\(f\)不如\(g\)优
    1. 若左端点处\(f\)更优,则\(f,g\)必然在左半区间产生交点,所以\(f\)只可能在左半区间优于\(g\),递归左儿子
    2. 若右端点处\(f\)更优,同理,递归右儿子
    3. 若都不优,返回即可
  • 若相交,归为\(f\)不如\(g\)

将\(g\)作为区间最优线段。

点此查看代码
#include<bits/stdc++.h>
#include<bits/extc++.h>
// using namespace __gnu_pbds;
// using namespace __gnu_cxx;
using namespace std;
#define infile(x) freopen(x,"r",stdin)
#define outfile(x) freopen(x,"w",stdout)
#define errfile(x) freopen(x,"w",stderr)
using ll=long long;using ull=unsigned long long;
using db = double;using ldb = long double;
#ifdef LOCAL
    FILE *InFile = infile("in.in"),*OutFile = outfile("out.out");
    // FILE *ErrFile=errfile("err.err");
#else
    FILE *Infile = stdin,*OutFile = stdout;
    //FILE *ErrFile = stderr;
#endif
const int N = 1e5 + 10,V = 40000;
const db eps = 1e-9;
#define pbi pair<db,int>
#define mk make_pair
struct Line{
    db k,b;
    inline db get_val(int x){return k*x+b;}
}s[N];int tot = 0;
inline bool cmp(int id1,int id2,int x){
    db val1 = s[id1].get_val(x),val2 = s[id2].get_val(x);
    if(val1 > val2 + eps) return true;
    if(val2 > val1 + eps) return false;
    return id1 < id2;
}
inline bool operator < (pbi a,pbi b){
    if(a.first + eps < b.first) return true;
    if(b.first + eps < a.first) return false;
    return a.second > b.second;
}
inline pbi Max(pbi a,pbi b){return a < b?b:a;}
class Segment_Tree{
private:
    struct segment_tree{
        int ls,rs,val;
        #define ls(x) tree[x].ls
        #define rs(x) tree[x].rs
        #define val(x) tree[x].val
    }tree[V<<1];
    int cnt = 0;
public:
    int rt;
    void update(int &k,int l,int r,int sl,int sr,int id){
        if(!k) k = ++cnt;
        if(sl <= l && r <= sr){
            bool cmpl = cmp(id,val(k),l),cmpr = cmp(id,val(k),r);
            if(cmpl && cmpr) return val(k) = id,void();
            if(!cmpl && !cmpr) return;
            int mid = (l+r)>>1;
            if(cmp(id,val(k),mid)) swap(val(k),id);
            if(cmp(id,val(k),l)) update(ls(k),l,mid,sl,sr,id);
            if(cmp(id,val(k),r)) update(rs(k),mid+1,r,sl,sr,id);
        }
        else{
            int mid = (l+r)>>1;
            if(sl <= mid) update(ls(k),l,mid,sl,sr,id);
            if(sr > mid) update(rs(k),mid+1,r,sl,sr,id);
        }
    }
    pbi query(int k,int l,int r,int x){
        if(!k) return mk(-1e18,-1);
        pbi res = mk(s[val(k)].get_val(x),val(k));
        if(l == r) return res;
        int mid = (l+r)>>1;
        if(x <= mid) res = Max(res,query(ls(k),l,mid,x));
        else res = Max(res,query(rs(k),mid+1,r,x));
        return res;
    }
}T;
signed main(){
    cin.tie(nullptr)->sync_with_stdio(false);
    cout.tie(nullptr)->sync_with_stdio(false);
    int n,lastans = 0;cin>>n;
    while(n--){
        int op;cin>>op;
        if(op){
            int x0,y0,x1,y1;
            cin>>x0>>y0>>x1>>y1;
            x0 = (x0 + lastans - 1) % 39989 + 1;
            x1 = (x1 + lastans - 1) % 39989 + 1;
            y0 = (y0 + lastans - 1) % 1000000000 + 1;
            y1 = (y1 + lastans - 1) % 1000000000 + 1;
            if(x0 > x1) swap(x1,x0),swap(y1,y0);
            tot++;
            if(x0 == x1) s[tot] = {0,max(1.0*y1,1.0*y0)};
            else{
                db k = 1.0*(y1-y0)/(x1-x0),b = y0 - k*x0;
                s[tot] = {k,b};
            }
            T.update(T.rt,1,40000,x0,x1,tot);
        }
        else{
            int x;cin>>x;x = (x + lastans - 1) % 39989 + 1;
            cout<<(lastans = T.query(T.rt,1,40000,x).second)<<'\n';
        }
    }
}

[JSOI2008] Blue Mary 开公司

这道题是全局修改,注意s不是截距,s-k才是

点此查看代码
#include<bits/stdc++.h>
#include<bits/extc++.h>
// using namespace __gnu_pbds;
// using namespace __gnu_cxx;
using namespace std;
#define infile(x) freopen(x,"r",stdin)
#define outfile(x) freopen(x,"w",stdout)
#define errfile(x) freopen(x,"w",stderr)
using ll=long long;using ull=unsigned long long;
using db = double;using ldb = long double;
#ifdef LOCAL
    FILE *InFile = infile("in.in"),*OutFile = outfile("out.out");
    // FILE *ErrFile=errfile("err.err");
#else
    FILE *Infile = stdin,*OutFile = stdout;
    //FILE *ErrFile = stderr;
#endif
const int N = 1e5 + 10,V = 5e4 + 10;
const db eps = 1e-9;
struct Line{
    db k,b;
    inline db get_val(int x){return k*x+b;}
}s[N];int tot = 0;
inline bool cmp(int id1,int id2,int x){
    db val1 = s[id1].get_val(x),val2 = s[id2].get_val(x);
    if(val1 > val2 + eps) return true;
    if(val2 > val1 + eps) return false;
    return id1 < id2;
}
inline db Max(db a,db b){
    if(a > b + eps) return a;
    else return b;
}
class Segment_Tree{
private:
    struct segment_tree{
        int ls,rs,val;
        #define ls(x) tree[x].ls
        #define rs(x) tree[x].rs
        #define val(x) tree[x].val
    }tree[V<<1];
    int cnt = 0;
public:
    int rt;
    void update(int &k,int l,int r,int id){
        if(!k) k = ++cnt;
        bool cmpl = cmp(id,val(k),l),cmpr = cmp(id,val(k),r);
        if(cmpl && cmpr) return val(k) = id,void();
        if(!cmpl && ! cmpr) return;
        int mid = (l + r)>>1;
        if(cmp(id,val(k),mid)) swap(id,val(k));
        if(cmp(id,val(k),l)) update(ls(k),l,mid,id);
        if(cmp(id,val(k),r)) update(rs(k),mid+1,r,id);
    }
    db query(int k,int l,int r,int x){
        if(!k) return 0;
        db res = s[val(k)].get_val(x);
        if(l == r) return res;
        int mid = (l+r)>>1;
        if(x <= mid) res = Max(res,query(ls(k),l,mid,x));
        else res = Max(res,query(rs(k),mid+1,r,x));
        return res;
    }
}T;
signed main(){
    cin.tie(nullptr)->sync_with_stdio(false);
    cout.tie(nullptr)->sync_with_stdio(false);
    int n;cin>>n;
    while(n--){
        string op;cin>>op;
        if(op[0] == 'P'){
            db b,k;cin>>b>>k;
            b -= k;
            s[++tot] = {k,b};
            T.update(T.rt,1,V-10,tot);
        }
        else{
            int x;cin>>x;
            int ans = T.query(T.rt,1,V-10,x);
            cout<<ans/100<<'\n';
        }
    }
}

当然,李超线段树还可以上树(与树剖放在一起)

可以参见[SDOI2016] 游戏

这个坑待填,主要是用处不是很大,像是为了凑在一起而强行凑在一起的。

如果我闲的没事就写了。

重头戏,李超线段树维护凸包,从而维护斜率优化dp

一道例题 : [CEOI2017] Building Bridges

dp式子

\[f_i=\min\{f_j+sum_{i-1}-sum_j+(h_i-h_j)^2\} \]

其中\(sum_n=\sum_{i=1}^nw_i\)

简单化一下,就是

\[f_i=-2h_ih_j+f_j+h_j^2-sum_j+sum_{i-1}+h_i^2 \]

\(sum_{i-1}+h_i^2\)为常数项,先扔出来不考虑。

我们发现上式就是一个\(y=kx+b\)的形式,其中

\[\begin{cases} y &= f_i\\ k &= -2h_j\\ x &= h_i\\ b &= h_j^2-sum_j+f_j \end{cases}\]

那么就是求\(x=h_i\)时的各个已经插入的函数最小值

然后就没了

注意inf要设极大,一开始插入一个\(k=0,b=inf\)的直线,开long long.

点此查看代码
#include<bits/stdc++.h>
#include<bits/extc++.h>
// using namespace __gnu_pbds;
// using namespace __gnu_cxx;
using namespace std;
#define infile(x) freopen(x,"r",stdin)
#define outfile(x) freopen(x,"w",stdout)
#define errfile(x) freopen(x,"w",stderr)
using ll=long long;using ull=unsigned long long;
using db = double;using ldb = long double;
#ifdef LOCAL
    FILE *InFile = infile("in.in"),*OutFile = outfile("out.out");
    // FILE *ErrFile=errfile("err.err");
#else
    FILE *Infile = stdin,*OutFile = stdout;
    //FILE *ErrFile = stderr;
#endif
const int N = 1e5 + 10,V = 1e6 + 10;
#define int long long
const int INF = 1e18;
int n,h[N],w[N],f[N];
struct Line{
    int k,b;
    inline int get_val(int x){return k*x+b;}
}s[N];int tot = 0;
inline bool cmp(int id1,int id2,int x){return s[id1].get_val(x) < s[id2].get_val(x);}
struct Segment_Tree{
private:
    struct segment_tree{
        int ls,rs,val;
        #define ls(x) tree[x].ls
        #define rs(x) tree[x].rs
        #define val(x) tree[x].val
    }tree[V<<1];
    int cnt = 0;
public:
    int rt;
    void update(int &k,int l,int r,int id){
        if(!k) k = ++cnt;
        bool cmpl = cmp(id,val(k),l),cmpr = cmp(id,val(k),r);
        if(cmpl && cmpr) return val(k) = id,void();
        if(!cmpl && !cmpr) return;
        int mid = (l+r)>>1;
        if(cmp(id,val(k),mid)) swap(val(k),id);
        if(l == r) return;
        if(cmp(id,val(k),l)) update(ls(k),l,mid,id);
        if(cmp(id,val(k),r)) update(rs(k),mid+1,r,id);
    }
    int query(int k,int l,int r,int x){
        if(!k) return INF;
        int res = s[val(k)].get_val(x);
        if(l == r) return res;
        int mid = (l+r)>>1;
        if(x <= mid) res = min(res,query(ls(k),l,mid,x));
        else res = min(res,query(rs(k),mid+1,r,x));
        return res;
    }
}T;
signed main(){
    cin.tie(nullptr)->sync_with_stdio(false);
    cout.tie(nullptr)->sync_with_stdio(false);
    cin>>n;
    for(int i = 1;i <= n; ++i) cin>>h[i];
    for(int i = 1;i <= n; ++i) cin>>w[i];
    for(int i = 1;i <= n; ++i) w[i] += w[i - 1];
    s[0] = {0,INF};
    f[1] = 0;
    s[1] = {-2*h[1],h[1]*h[1]-w[1]+f[1]};
    T.update(T.rt,0,1e6,1);
    for(int i = 2;i <= n; ++i){
        f[i] = T.query(1,0,1e6,h[i]) + w[i - 1] + h[i] * h[i];
        s[i] = {-2*h[i],h[i]*h[i]-w[i]+f[i]};
        T.update(T.rt,0,1e6,i);
    }
    cout<<f[n]<<'\n';
}

标签:return,val,int,线段,李超,long,using,define
From: https://www.cnblogs.com/hzoi-Cu/p/18328910

相关文章

  • G73 线段树+线性基合并 P5607 [Ynoi2013] 无力回天 NOI2017
    视频链接:G73线段树+线性基合并P5607[Ynoi2013]无力回天NOI2017_哔哩哔哩_bilibili   P5607[Ynoi2013]无力回天NOI2017-洛谷|计算机科学教育新生态(luogu.com.cn)//线段树+线性基合并O(n*logn*logv*logv)#include<iostream>#include<cstring>#incl......
  • 浅谈简单的数据结构1(树状数组 、线段树)(c++)
    *_*课间休息后的知识点轰炸树状数组引入给定长为n的序列a,q次操作,每次查询一段区间的和,或修改一个数的权值。1≤n,q≤5×10^5,0≤a_i≤10^9。分析如果没有修改操作,这是一道非常简单的前缀和题。假如我们修改前缀和数组,查询就还是O(1)的,是否可行呢?当然不行。考虑......
  • C++题解(17) 狐猬编程: 640.线段覆盖
    题目描述在一条数轴上,有N条线段,第i条线段的左端点是s[i],右端点是e[i]。如果线段有重叠(即使是端点重叠也算是重叠),则输出“impossible”,如果没有重叠则输出“possible”。输入格式输入文件名:640.in多组测试数据。第一行,一个整数G,表示有G组测试数据。1<=G<=10。每组......
  • C141 线段树分治+线性基 P3733 [HAOI2017] 八纵八横
    视频链接:C141线段树分治+线性基P3733[HAOI2017]八纵八横_哔哩哔哩_bilibili   P3733[HAOI2017]八纵八横-洛谷|计算机科学教育新生态(luogu.com.cn)//线段树分治+线性基O(q*logq*logL*logL)#include<iostream>#include<cstring>#include<algorithm>......
  • 【学习笔记】线段树
    本文Markdown源代码冲刺\(3000\)行中,目前行数:\(2678\)行。【0】线段树简介【0.1】线段树是干什么的线段树是一种基于分治的树形数据结构,可以处理很多区间问题,值域问题。【0.2】线段树的形态线段树作为一棵二叉树,其左子节点维护的是左半区间的信息,右子节点维护的是右半区......
  • 282:vue+openlayers 利用 LineString 显示线段
    作者:还是大剑师兰特,曾为美国某知名大学计算机专业研究生,现为国内GIS领域高级前端工程师,CSDN知名博主,深耕openlayers、leaflet、mapbox、cesium,canvas,echarts等技术开发,欢迎加微信(gis-dajianshi),一起交流。查看本专栏目录-本文是第282个示例文章目录一......
  • 线段树入门
    【模板】线段树1题目描述如题,已知一个数列,你需要进行下面两种操作:将某区间每一个数加上\(k\)。求出某区间每一个数的和。输入格式第一行包含两个整数\(n,m\),分别表示该数列数字的个数和操作的总个数。第二行包含\(n\)个用空格分隔的整数,其中第\(i\)个数字表示数......
  • 线段树扩展学习
    前言来补一下暑期集训的坑。正文标记可持久化这个很好理解,在进行区间修改的时候,不下传懒标记,查询的时候直接对每一层再进行处理即可。这个主要用于线段树分治,所以理解就行,代码不给了。动态开点之前一直不太会,现在来补一下。这种线段树主要用于优化空间复杂度。就是对于下......
  • 线段树(区间操作,例题:洛谷P3372 线段树 1)
    在上一节线段树(原理、构造和区间查询,例题:BalancedLineup)中介绍了线段树的构造,下面就来说一下它的区间操作。区间操作与Lazy-Tag有关,如果修改操作是对区间内的每个元素一一修改,就会比较繁琐低效,目前的解决办法是线段树的tree[i].data记录的是区间i的值(详细见上节),可以再定义一......
  • 线段树
    自己看:https://blog.csdn.net/weq2011/article/details/128791426看懂就没问题了  单点修改+区间查询 https://www.luogu.com.cn/problem/P3374#include<bits/stdc++.h>#defineintlonglongusingnamespacestd;constintN=5e5+5;intn,m,x,y,op,w[N],......