文章目录
用来处理多个有 斜率 与 截距 的线段在区间上的 极值问题 .
普通线段树的节点一般直接保存区间和, 极值等信息,
李超线段树的节点保存过这个 区间中点最优 的直线, 注意是 区间中点最优,
意思就是: 查询 x 点的最优值时, 必须递归到底部叶子节点, 才可以获取 “单点 x 过哪个线段才能获得最优值” 的信息 .
插入节点↓
将算法用 文字 加 分支结构 描述如下 ↓
- 新加入的线段斜率大于原来线段斜率
- 新线段在右区间完全优于原有线段, 将原有线段贬斥到左子树, 原有线段只可能在左子树优于新线段
- 否则只有更右边有替换原线段的希望, 递归右区间 .
- 新加入的线段斜率小于等于原来线段斜率
- 新线段在做区间完全优于原有线段, 将原有线段贬斥到右子树, 原有线段只可能在右子树优于新线段
- 否则只有更左边有替换原线段的希望, 递归左区间 .
总体来说: 插入节点 相当于拿一个线段不断 试, 找到能够顶替的线段就顶替,否则继续向下深入尝试.
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: [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\n", (int)Query(1, x)/100);
}
}
return 0;
}