Loading

【数据结构】【模板】李超线段树

李超线段树

定义

可以看看洛谷的模板题目:

image

作用

  1. 优化动态规划,如果可以将一个动态规划的转移式子转化为 \(y = kx + b\) 的形式,那么我们可以边转移边将 \(y = kx + b\) 这条线段放入李超线段树,然后在下次转移时,直接调用下次计算出来的 \(x\) 位置上的最大值或最小值。(结合题目理解)

构建方式

以洛谷模板题为例。

插入线段

对于线段树上的区间 \([l, r]\),保存一个线段编号,定义 \(p(x)\) 表示线段 \(p\) 在横坐标为 \(x\) 时的纵坐标。

当有新的线段来时,以以下方式更新。

  1. 取区间 \([l, r]\) 的中点 \(mid\)
  2. 看看将 \(mid\) 作为横坐标时,保存的线段 \(id\) 和新来的线段 \(seg\) 谁大,如果 \(seg(mid)\)\(id(mid)\) 大,那么交换 \(id\)\(seg\),这时 \(id(mid) < seg(mid)\)
  3. 接下来我们看看 \(id\) 这条线段是否还有可能成为左右区间的代表线段,看看做左端点谁大,如果 \(id(l)\)\(seg(l)\) 更大,那么递归更新区间 \([l, mid]\),线段为 \(id\)
    image
  4. 然后再看看右区间,如果 \(id(r)\)\(seg(r)\) 更大,那么递归更新区间 \([mid + 1, r]\),线段为 \(id\)
    image

因为 3. 4 中的条件只有一种可能实现,这也说明了:

  1. 只有一种情况会往下递归;
  2. 不能用 pushdown;
  3. 时间复杂度为 \(O(\log^2 n)\)

查询线段

我们可以按照线段树区间查询的顺序,从上往下按照取出的线段,比较其在横坐标为 \(x\) 的大小。

模板代码

P4097

#include <bits/stdc++.h>

using namespace std;

const int N = 100010;
const double eps = 1e-6;

int cmp(double x, double y) {
    if (fabs(x - y) < eps) return 0;
    if (x < y) return -1;
    return 1;
}

struct node {
    int id;
} tr[N << 2];

double gety(double k, int x, double b) {
    return k * x + b;
}

double k[N], b[N];

bool compare(int v1, int v2, int x) {
    double y1 = gety(k[v1], x, b[v1]);
    double y2 = gety(k[v2], x, b[v2]);
    if (!cmp(y1, y2)) return v1 < v2;
    return cmp(y1, y2) == 1;
}

void update(int u, int l, int r, int pl, int pr, int x) {
    int mid = l + r >> 1;
    if (pl <= l && r <= pr) {
        if (!tr[u].id) {
            tr[u].id = x;
            return;
        }
        if (compare(x, tr[u].id, mid)) swap(tr[u].id, x);
        if (compare(x, tr[u].id, l)) update(u << 1, l, mid, pl, pr, x);
        if (compare(x, tr[u].id, r)) update(u << 1 | 1, mid + 1, r, pl, pr, x);
        return;
    }
    if (pl <= mid) update(u << 1, l, mid, pl, pr, x);
    if (pr > mid) update(u << 1 | 1, mid + 1, r, pl, pr, x);
}

int query(int u, int l, int r, int x) {
    if (l == r) return tr[u].id;
    int mid = l + r >> 1;
    int ans = tr[u].id;
    if (x <= mid) {
        int ans2 = query(u << 1, l, mid, x);
        if (compare(ans, ans2, x)) return ans;
        else return ans2;
    }
    else {
        int ans2 = query(u << 1 | 1, mid + 1, r, x);
        if (compare(ans, ans2, x)) return ans;
        else return ans2;
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int q;
    cin >> q;
    int opt, x1, y1, x2, y2, lastans = 0, cnt = 0;
    for (int i = 1; i <= q; i++) {
        cin >> opt;
        if (opt == 0) {
            cin >> x1;
            x1 = (x1 + lastans - 1) % 39989 + 1;
            cout << (lastans = query(1, 1, 40000, x1)) << '\n';
        }
        else {
            cin >> x1 >> y1 >> x2 >> y2;
            x1 = (x1 + lastans - 1) % 39989 + 1;
            x2 = (x2 + lastans - 1) % 39989 + 1;
            y1 = (y1 + lastans - 1) % 1000000000 + 1;
            y2 = (y2 + lastans - 1) % 1000000000 + 1;

            if (x1 > x2) swap(x1, x2), swap(y1, y2);
            cnt++;
            if (x1 == x2) b[cnt] = max(y1, y2);
            else {
                k[cnt] = 1.0 * (y1 - y2) / (x1 - x2);
                b[cnt] = y1 - k[cnt] * x1;
            }
            update(1, 1, 40000, x1, x2, cnt);
        }
    }
    return 0;
}

例题

P4254 [JSOI2008] Blue Mary 开公司(板题)

MyBlog: P4655 [CEOI2017] Building Bridges

P8726 [蓝桥杯 2020 省 AB3] 旅行家 类似 P4655 做法。

MyBlog: P4069 [SDOI2016] 游戏

MyBlog: P2900 [USACO08MAR] Land Acquisition G

posted @ 2024-08-28 09:03  SunnyYuan  阅读(1)  评论(0编辑  收藏  举报