李超线段树

李超线段树

  • 用途 : 在平面上插入一个线段或直线,求出与\(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';
}

[SDOI2012] 任务安排

式子不是很板子,用到了一个费用提前计算的思想

\[f_i=f_j+s\times (sc_n-sc_j)+st_i\times(sc_i-sc_j) \]

化一下,就是

\[f_i=-st_isc_j+f_j-s\times sc_j + st_i\times sc_i + s\times sc_n \]

然后和上一道题一样了。

但是我们发现\(st_i\)会有负的值,且值域很大,考虑离散化一下。

就没了

点此查看代码
#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 = 3e5 + 10,V = 3e5 + 10;
#define int long long
const int INF = 1e18;
struct Line{
    int k,b;
    inline int get_val(int x){return k*x+b;}
}s[N];
int n,S,t[N],c[N],pos[N],f[N],num[N];
inline bool cmp(int id1,int id2,int x){
    return s[id1].get_val(x) < s[id2].get_val(x);
}
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[N<<2];
    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),num[l]),cmpr = cmp(id,val(k),num[r]);
        if(cmpl && cmpr) return val(k) = id,void();
        if(!cmpl && !cmpr) return;
        int mid = (l+r)>>1;
        if(cmp(id,val(k),num[mid])) swap(val(k),id);
        if(cmp(id,val(k),num[l])) update(ls(k),l,mid,id);
        if(cmp(id,val(k),num[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(num[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>>S;s[0].b = INF;
    for(int i = 1;i <= n; ++i) cin>>t[i]>>c[i];
    for(int i = 1;i <= n; ++i) t[i] += t[i - 1];
    for(int i = 1;i <= n; ++i) c[i] += c[i - 1];
    for(int i = 1;i <= n; ++i) num[i] = t[i];
    sort(num+1,num+1+n);int m = unique(num+1,num+1+n)-num-1;
    for(int i = 1;i <= n; ++i) pos[i] = lower_bound(num+1,num+1+m,t[i]) - num;
    f[1] = t[1]*c[1]+S*c[n],s[1] = {-c[1],f[1]+S*(c[n]-c[1])};
    T.update(T.rt,1,m,1);
    for(int i = 2;i <= n; ++i){
        f[i] = t[i]*c[i]+S*c[n];
        f[i] = min(f[i],T.query(1,1,m,pos[i])+t[i]*c[i]);
        s[i] = {-c[i],f[i] + S*(c[n]-c[i])};
        T.update(T.rt,1,m,i);
    }
    cout<<f[n];
}
posted @ 2024-07-29 21:24  CuFeO4  阅读(9)  评论(0编辑  收藏  举报