当前位置:首页 > 技术 > 正文内容

树套树的应用与实现

访客 技术 2026年5月27日 3

树套树是一种数据结构嵌套的方式,通常用于处理复杂查询。外层可以是线段树或树状数组,而内层则可以是平衡树或线段树。

常用组合:

  • 外层:线段树、树状数组
  • 内层:平衡树、线段树(通常使用标准库)

示例题解

示例题目一

这是一个简单的线段树套set的例子:

#include <iostream>
#include <set>
using namespace std;

const int MAXN = 50005, MAXM = MAXN * 4;
int n, m;
struct SegmentTree {
    int l, r;
    multiset<int> s;
} tree[MAXM];
int w[MAXN];

void build(int u, int l, int r) {
    tree[u] = {l, r};
    tree[u].s.insert(INT_MIN);
    tree[u].s.insert(INT_MAX);
    for (int i = l; i <= r; ++i)
        tree[u].s.insert(w[i]);
    if (l == r) return;
    int mid = (l + r) / 2;
    build(u << 1, l, mid);
    build(u << 1 | 1, mid + 1, r);
}

void update(int u, int p, int x) {
    tree[u].s.erase(tree[u].s.find(w[p]));
    tree[u].s.insert(x);
    if (tree[u].l == tree[u].r) return;
    int mid = (tree[u].l + tree[u].r) / 2;
    if (p <= mid)
        update(u << 1, p, x);
    else
        update(u << 1 | 1, p, x);
}

int query(int u, int a, int b, int x) {
    if (tree[u].l >= a && tree[u].r <= b) {
        auto it = --tree[u].s.lower_bound(x);
        return *it;
    }
    int mid = (tree[u].l + tree[u].r) / 2;
    int res = INT_MIN;
    if (a <= mid)
        res = max(res, query(u << 1, a, b, x));
    if (b > mid)
        res = max(res, query(u << 1 | 1, a, b, x));
    return res;
}

int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; ++i)
        cin >> w[i];
    build(1, 1, n);

    while (m--) {
        int op, a, b, x;
        cin >> op;
        if (op == 1) {
            cin >> a >> x;
            update(1, a, x);
            w[a] = x;
        } else {
            cin >> a >> b >> x;
            cout << query(1, a, b, x) << endl;
        }
    }
    return 0;
}

示例题目二

这是另一个示例,展示了如何使用线段树套平衡树来解决更复杂的问题:

#include <iostream>
#include <algorithm>
using namespace std;

const int MAXN = 2000005, INF = 2147483647;
int n, m;
struct Node {
    int child[2], parent, value;
    int size;
    void init(int v, int p) {
        value = v;
        parent = p;
        size = 1;
    }
} nodes[MAXN];
int leftChild[MAXN], rightChild[MAXN], root[MAXN], indexCounter;
int values[MAXN];

void pushUp(int x) {
    nodes[x].size = nodes[nodes[x].child[0]].size + nodes[nodes[x].child[1]].size + 1;
}

void rotate(int x) {
    int y = nodes[x].parent, z = nodes[y].parent;
    int k = nodes[y].child[1] == x;
    nodes[z].child[nodes[z].child[1] == y] = x;
    nodes[x].parent = z;
    nodes[y].child[k] = nodes[x].child[k ^ 1];
    nodes[nodes[x].child[k ^ 1]].parent = y;
    nodes[x].child[k ^ 1] = y;
    nodes[y].parent = x;
    pushUp(y);
    pushUp(x);
}

void splay(int &root, int x, int target) {
    while (nodes[x].parent != target) {
        int y = nodes[x].parent, z = nodes[y].parent;
        if (z != target)
            rotate((nodes[y].child[1] == x) ^ (nodes[z].child[1] == y) ? x : y);
        rotate(x);
    }
    if (!target)
        root = x;
}

void insert(int &root, int val) {
    int node = root, parent = 0;
    while (node)
        parent = node, node = nodes[node].child[val > nodes[node].value];
    node = ++indexCounter;
    if (parent)
        nodes[parent].child[val > nodes[parent].value] = node;
    nodes[node].init(val, parent);
    splay(root, node, 0);
}

int getKth(int root, int val) {
    int node = root, rank = 0;
    while (node) {
        if (nodes[node].value < val)
            rank += nodes[nodes[node].child[0]].size + 1, node = nodes[node].child[1];
        else
            node = nodes[node].child[0];
    }
    return rank;
}

void modify(int &root, int oldVal, int newVal) {
    int node = root;
    while (node) {
        if (nodes[node].value == oldVal)
            break;
        if (nodes[node].value < oldVal)
            node = nodes[node].child[1];
        else
            node = nodes[node].child[0];
    }
    splay(root, node, 0);
    int left = nodes[node].child[0], right = nodes[node].child[1];
    while (nodes[left].child[1])
        left = nodes[left].child[1];
    while (nodes[right].child[0])
        right = nodes[right].child[0];
    splay(root, left, 0), splay(root, right, left);
    nodes[right].child[0] = 0;
    pushUp(left), pushUp(right);
    insert(root, newVal);
}

void build(int u, int l, int r) {
    leftChild[u] = l, rightChild[u] = r;
    insert(root[u], INF), insert(root[u], -INF);
    for (int i = l; i <= r; ++i)
        insert(root[u], values[i]);
    if (l == r) return;
    int mid = (l + r) / 2;
    build(u << 1, l, mid);
    build(u << 1 | 1, mid + 1, r);
}

int query(int u, int a, int b, int x) {
    if (leftChild[u] >= a && rightChild[u] <= b)
        return getKth(root[u], x) - 1;
    int mid = (leftChild[u] + rightChild[u]) / 2;
    int result = 0;
    if (a <= mid)
        result += query(u << 1, a, b, x);
    if (b > mid)
        result += query(u << 1 | 1, a, b, x);
    return result;
}

void change(int u, int p, int x) {
    modify(root[u], values[p], x);
    if (leftChild[u] == rightChild[u]) return;
    int mid = (leftChild[u] + rightChild[u]) / 2;
    if (p <= mid)
        change(u << 1, p, x);
    else
        change(u << 1 | 1, p, x);
}

int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; ++i)
        cin >> values[i];
    build(1, 1, n);

    while (m--) {
        int op, a, b, x;
        cin >> op;
        if (op == 1) {
            cin >> a >> b >> x;
            cout << query(1, a, b, x) + 1 << endl;
        } else if (op == 2) {
            cin >> a >> b >> x;
            int low = 0, high = 1e8;
            while (low < high) {
                int mid = (low + high + 1) / 2;
                if (query(1, a, b, mid) + 1 <= x)
                    low = mid;
                else
                    high = mid - 1;
            }
            cout << high << endl;
        } else if (op == 3) {
            cin >> a >> x;
            change(1, a, x);
            values[a] = x;
        } else if (op == 4) {
            cin >> a >> b >> x;
            cout << getPredecessor(root[1], a, b, x) << endl;
        } else {
            cin >> a >> b >> x;
            cout << getSuccessor(root[1], a, b, x) << endl;
        }
    }
    return 0;
}

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。