李超线段树学习笔记 [模板]

  • 线李超线段树的作用

用来处理多个有 斜率截距 的线段在区间上的 极值问题 .

  • 线李超线段树的内容

普通线段树的节点一般直接保存区间和, 极值等信息,

李超线段树的节点保存过这个 区间中点最优 的直线, 注意是 区间中点最优,

意思就是: 查询 xx 点的最优值时, 必须递归到底部叶子节点, 才可以获取 “单点 xx 过哪个线段才能获得最优值” 的信息 .


  • 线李超线段树的实现

插入节点 ↓

将算法用 文字 加 分支结构 描述如下

  • 新加入的线段斜率大于原来线段斜率
    • 新线段在右区间完全优于原有线段, 将原有线段贬斥到左子树, 原有线段只可能在左子树优于新线段
    • 否则只有更右边有替换原线段的希望, 递归右区间 .
  • 新加入的线段斜率小于等于原来线段斜率
    • 新线段在做区间完全优于原有线段, 将原有线段贬斥到右子树, 原有线段只可能在右子树优于新线段
    • 否则只有更左边有替换原线段的希望, 递归左区间 .

总体来说: 插入节点 线相当于拿一个线段不断 , 线,.找到能够顶替的线段就顶替, 否则继续向下深入尝试 .

void Modify(int k, int l, int r, int x){
	T[k].l = l, T[k].r = r;
        if(l == r){
                if(get_val(x, l) > get_val(T[k].xd, l)) T[k].xd = x;
                return ;
        }
        int mid = l+r >> 1;
        if(slop[T[k].xd] < slop[x]){ // 新加入的线段斜率大于原来线段斜率
                if(get_val(x, mid) > get_val(T[k].xd, mid)){ // 新线段在右区间完全优于原有线段
                        Modify(k<<1, l, mid, T[k].xd);       // 将原有线段贬斥到左子树, 原有线段只可能在右子树优于新线段
                        T[k].xd = x;
                }else   Modify(k<<1|1, mid+1, r, x);         // 否则只有更右边有替换原线段的希望
        }else{ //新加入的线段斜率小于等于原来线段斜率
                if(get_val(x, mid) > get_val(T[k].xd, mid)){ // 新线段在做区间完全优于原有线段
                        Modify(k<<1|1, mid+1, r, T[k].xd);   // 将原有线段贬斥到右子树, 原有线段只可能在右子树优于新线段
                        T[k].xd = x;
                }else   Modify(k<<1, l, mid, x);             // 否则只有更左边有替换原线段的希望
        }
}
单点查询 ↓
double Query(int k, int x){
	int l = T[k].l, r = T[k].r;
        if(l == r) return get_val(T[k].xd, x);
        int mid = l+r >> 1;
        if(x <= mid) return std::max(get_val(T[k].xd, x), Query(k<<1, x));
        return std::max(get_val(T[k].xd, x), Query(k<<1|1, x));
}

  • 线李超线段树的应用

1:例题1: [JSOI2008]Blue Mary开公司

上方代码拼凑起来即为这道题 .

#include<bits/stdc++.h>

const int maxn = 50004;

int N;
int cnt;
double slop[maxn<<1];
double b[maxn<<1];

struct Node{ int l, r, xd; } T[maxn<<2];

double get_val(int cnt, int x){ return slop[cnt]*(x-1) + b[cnt]; }

void Modify(int k, int l, int r, int x){
	T[k].l = l, T[k].r = r;
        if(l == r){
                if(get_val(x, l) > get_val(T[k].xd, l)) T[k].xd = x;
                return ;
        }
        int mid = l+r >> 1;
        if(slop[T[k].xd] < slop[x]){ // 新加入的线段斜率大于原来线段斜率
                if(get_val(x, mid) > get_val(T[k].xd, mid)){ // 新线段在右区间完全优于原有线段
                        Modify(k<<1, l, mid, T[k].xd);       // 将原有线段贬斥到左子树, 原有线段只可能在右子树优于新线段
                        T[k].xd = x;
                }else   Modify(k<<1|1, mid+1, r, x);         // 否则只有更右边有替换原线段的希望
        }else{ //新加入的线段斜率小于等于原来线段斜率
                if(get_val(x, mid) > get_val(T[k].xd, mid)){ // 新线段在做区间完全优于原有线段
                        Modify(k<<1|1, mid+1, r, T[k].xd);   // 将原有线段贬斥到右子树, 原有线段只可能在右子树优于新线段
                        T[k].xd = x;
                }else   Modify(k<<1, l, mid, x);             // 否则只有更左边有替换原线段的希望
        }
}

double Query(int k, int x){
	int l = T[k].l, r = T[k].r;
        if(l == r) return get_val(T[k].xd, x);
        int mid = l+r >> 1;
        if(x <= mid) return std::max(get_val(T[k].xd, x), Query(k<<1, x));
        return std::max(get_val(T[k].xd, x), Query(k<<1|1, x));
}

int main(){
        scanf("%d", &N);
        while(N --){
                char opt[5];
                scanf("%s", opt);
                if(opt[0] == 'P'){
                        cnt ++;
                        scanf("%lf%lf", &b[cnt], &slop[cnt]);
                        Modify(1, 1, maxn, cnt);
                }else{
                        int x;
                        scanf("%d", &x);
                        printf("%d
", (int)Query(1, x)/100);
                }
        }
        return 0;
}
原文地址:https://www.cnblogs.com/zbr162/p/11822540.html