李超线段树学习笔记

李超线段树支持这样的操作:

  1. 插入一条直线。
  2. 询问与$x=x_0$相交的点的最大/小的纵坐标(接下来我们以最大值为例)

它基于线段树的标记永久化,也就是说,我们对于区间$[l,r]$维护$x=mid$上最高的直线的编号(但是如果$[l,r]$的所有点都以$[l,r]$的父亲记录的直线作为最大值,那么$[l,r]$的子树都没有记录的直线(这就是标记永久化)),然后询问操作肯定没有问题,重点就是添加直线了。

假设当前递归到$[l,r]$,要插入$k_1x+b_1$,之前记录的直线为$k_2x+b_2$。

不妨设$k_1<k_2$,$k_1\geq k_2$的情况同理。

第一种情况:$k_1mid+b_1>k_2mid+b_2$,那么更新$[l,r]$的标记,只向$[mid+1,r]$递归,因为$[l,mid]$上所有点都以$k_1x+b_1$作为最大值。

第二种情况:$k_1mid+b_1\leq k_2mid+b_2$,那么不更新$[l,r]$的标记,只向$[l,mid]$递归,因为$[mid+1,r]$上所有点都以$k_2x+b_2$作为最大值。

这样显然更新一次是$O(\log^2 n)$的。(我也不知道我在想什么。。。)

#include<bits/stdc++.h>
#define Rint register int
using namespace std;
const int N = 100003, M = 50000;
int n, tot;
struct Line {
    double k, b;
    inline double val(int x){return k * (x - 1) + b;}
} l[N];
int tag[M << 2];
inline double query(int x, int L, int R, int pos){
    if(L == R) return l[tag[x]].val(L);
    int mid = L + R >> 1;
    double ans = l[tag[x]].val(pos);
    if(pos <= mid) ans = max(ans, query(x << 1, L, mid, pos));
    else ans = max(ans, query(x << 1 | 1, mid + 1, R, pos));
    return ans;
}
inline void insert(int x, int L, int R, int id){
    if(L == R){
        if(l[tag[x]].val(L) < l[id].val(L)) tag[x] = id;
        return;
    }
    int mid = L + R >> 1;
    if(l[tag[x]].k < l[id].k){
        if(l[tag[x]].val(mid) < l[id].val(mid)){insert(x << 1, L, mid, tag[x]); tag[x] = id;}
        else insert(x << 1 | 1, mid + 1, R, id);
    } else {
        if(l[tag[x]].val(mid) < l[id].val(mid)){insert(x << 1 | 1, mid + 1, R, tag[x]); tag[x] = id;}
        else insert(x << 1, L, mid, id);
    }
}
int main(){
    scanf("%d", &n);
    while(n --){
        char s[10]; scanf("%s", s);
        if(s[0] == 'Q'){
            int t;
            scanf("%d", &t);
            printf("%d\n", (int) query(1, 1, M, t) / 100);
        } else {
            ++ tot;
            scanf("%lf%lf", &l[tot].b, &l[tot].k);
            insert(1, 1, M, tot);
        }
    }
}
Luogu4254

例题:CF1175G(如果认为难度落差太大,本人不负责)

posted @ 2019-06-19 18:04  mizu164  阅读(236)  评论(3编辑  收藏  举报