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

线段树的区间操作与查询机制

访客 技术 2026年6月29日 1

在线段树中,区间操作主要涉及两种核心机制:向上更新(pushup)和向下传递(pushdown)。这些操作在处理区间查询和更新时至关重要。

向上更新(Pushup)

当子节点的数据发生变化时,需要通过pushup操作更新父节点。这种操作通常在构建树、修改节点值或查询结果时使用。与简单返回节点值不同,pushup可能需要结合左右子节点的信息进行复杂计算。

向下传递(Pushdown)

当父节点的值发生变化时,需要通过pushdown操作将修改传递给子节点。这种操作通常在处理延迟更新时使用,确保子节点在后续操作中能够正确反映父节点的修改。

区间最大连续和查询

为了实现区间最大连续和查询,线段树的每个节点需要维护以下信息:

  • lmax:左子区间最大连续和
  • rmax:右子区间最大连续和
  • tmax:当前区间最大连续和
  • sum:区间总和

通过结合左右子节点的信息,可以计算出当前节点的最大连续和。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 500010;

int n, m;
int w[N];
struct Node {
    int l, r;
    int total, leftMax, rightMax, maxSum;
} tree[N * 4];

void merge(Node &parent, Node &left, Node &right) {
    parent.total = left.total + right.total;
    parent.leftMax = max(left.leftMax, left.total + right.leftMax);
    parent.rightMax = max(right.rightMax, right.total + left.rightMax);
    parent.maxSum = max(left.maxSum, right.maxSum, left.rightMax + right.leftMax);
}

void pushUp(int index) {
    merge(tree[index], tree[index << 1], tree[index << 1 | 1]);
}

void build(int index, int l, int r) {
    tree[index] = {l, r};
    if (l == r) {
        tree[index].total = w[r];
        tree[index].leftMax = tree[index].rightMax = tree[index].maxSum = w[r];
        return;
    }
    int mid = l + r >> 1;
    build(index << 1, l, mid);
    build(index << 1 | 1, mid + 1, r);
    pushUp(index);
}

// 其余代码省略...

区间最大公约数查询

通过构建差分数组并维护最大公约数,可以实现区间最大公约数查询。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>

using namespace std;

typedef long long LL;

const int N = 500010;

int n, m;
LL w[N];
struct Node {
    int l, r;
    LL diff, gcdVal;
} tree[N * 4];

LL gcd(LL a, LL b) {
    return b ? gcd(b, a % b) : a;
}

void merge(Node &parent, Node &left, Node &right) {
    parent.diff = left.diff + right.diff;
    parent.gcdVal = gcd(left.gcdVal, right.gcdVal);
}

void pushUp(int index) {
    merge(tree[index], tree[index << 1], tree[index << 1 | 1]);
}

void build(int index, int l, int r) {
    if (l == r) {
        LL diff = w[r] - w[r - 1];
        tree[index] = {l, r, diff, diff};
    } else {
        tree[index].l = l;
        tree[index].r = r;
        int mid = l + r >> 1;
        build(index << 1, l, mid);
        build(index << 1 | 1, mid + 1, r);
        pushUp(index);
    }
}

// 其余代码省略...

区间修改与懒标记

为了支持高效的区间修改,线段树需要支持懒标记传播。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

#define LL long long

const int N = 100010;

int n, m;
int w[N];
struct Node {
    int l, r;
    LL total;
    LL add;
} tree[N * 4];

void pushDown(int index) {
    if (tree[index].add != 0) {
        int left = index << 1;
        int right = index << 1 | 1;
        tree[left].add += tree[index].add;
        tree[left].total += (tree[left].r - tree[left].l + 1) * tree[index].add;
        tree[right].add += tree[index].add;
        tree[right].total += (tree[right].r - tree[right].l + 1) * tree[index].add;
        tree[index].add = 0;
    }
}

void build(int index, int l, int r) {
    tree[index] = {l, r, 0, 0};
    if (l == r) {
        tree[index].total = w[r];
        return;
    }
    int mid = l + r >> 1;
    build(index << 1, l, mid);
    build(index << 1 | 1, mid + 1, r);
    tree[index].total = tree[index << 1].total + tree[index << 1 | 1].total;
}

// 其余代码省略...

维护序列问题

通过维护序列的模运算特性,可以实现复杂的数据结构操作。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;
#define LL long long

const int N = 100010;

int n, p, m;
int w[N];
struct Node {
    int l, r;
    LL total;
    LL add, mul;
} tree[N * 4];

void apply(Node &node, LL add, LL mul) {
    node.total = (node.total * mul + add * (node.r - node.l + 1)) % p;
    node.mul = node.mul * mul % p;
    node.add = (node.add * mul + add) % p;
}

void pushDown(int index) {
    Node &node = tree[index];
    if (node.add != 0 || node.mul != 1) {
        int left = index << 1;
        int right = index << 1 | 1;
        apply(tree[left], node.add, node.mul);
        apply(tree[right], node.add, node.mul);
        node.add = 0;
        node.mul = 1;
    }
}

void build(int index, int l, int r) {
    tree[index] = {l, r, 0, 0, 1};
    if (l == r) {
        tree[index].total = w[r];
        return;
    }
    int mid = l + r >> 1;
    build(index << 1, l, mid);
    build(index << 1 | 1, mid + 1, r);
    tree[index].total = (tree[index << 1].total + tree[index << 1 | 1].total) % p;
}

// 其余代码省略...

应用场景

在线段树中,这些操作可以高效处理以下场景:

  • 区间乘法
  • 区间加法
  • 区间最大值查询
  • 区间求和
  • 区间最大公约数查询
  • 复杂模运算序列维护
返回列表

上一篇:C#批量下载图片与HTML转Word技术实践

没有最新的文章了...

相关文章

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...

发表评论

访客

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