线段树合并及其相关问题

一、线段树合并题目

线段树是一种常用的数据结构,在解决区间查询、修改问题时非常方便。但是,在实际的问题中,我们常常需要对两个不同的线段树进行合并,以便更好地完成某些操作。典型的线段树合并题目包括P3834 【模板】动态开点线段树和P3835 【模板】动态开点线段树 2。在这些问题中,我们需要实现线段树的合并、修改、查询等操作,并保证时间和空间效率。下面我们将详细介绍线段树的合并策略及其相关问题。

二、线段树合并max卷积

max卷积是一种常见的操作,它可以将两个长度为n的序列A和B卷积成长度为n的序列C,其中C[i] = max(A[j] + B[i-j+1])。这个操作可以通过线段树实现。我们首先将A和B分别建立线段树,然后按照递归合并的方法来合并这两个线段树。在合并过程中,我们需要记录下每个区间内A和B的最大值,以便计算C。具体的代码示例如下:

const int N = 1e5 + 5;

struct SegmentTree {
    int l, r;
    int mx[N << 2], val[N << 2];

    inline void pushup(int o) {
        mx[o] = max(mx[o << 1], mx[o <l = l, this->r = r;
        if (l == r) {
            val[o] = mx[o] = read();
            return;
        }
        int mid = (l + r) >> 1;
        build(o << 1, l, mid);
        build(o <> 1;
        if (p <= mid)
            update(o << 1, p, v);
        else
            update(o << 1 | 1, p, v);
        pushup(o);
    }

    int query(int o, int ql, int qr) {
        if (ql = r)
            return mx[o];
        int mid = (l + r) >> 1;
        int ret = 0;
        if (ql <= mid)
            ret = max(ret, query(o < mid)
            ret = max(ret, query(o << 1 | 1, ql, qr));
        return ret;
    }
};

int n, m, a[N], b[N];
SegmentTree sa, sb;

int main() {
    n = read(), m = read();
    sa.build(1, 1, n);
    sb.build(1, 1, n);
    for (int i = 1; i = 0; --j) {
                int tmp = ans | (1 <= a[x] + b[y])
                    ans = tmp;
            }
            print(ans);
        }
    }
    return 0;
}

三、线段树合并 刘汝佳

刘汝佳在线段树合并问题中提出了一个有趣的做法,即将两个线段树的节点按照层次进行配对,然后用一个vector来记录配对结果。在递归合并两个线段树的过程中,我们先判断两个节点是否在同一层,若是则将它们配对,否则将两棵子树进一步递归合并。这种方法在时间复杂度和空间复杂度上都有优化。具体的代码示例如下:

const int N = 100005;

struct SegmentTree {
    int l, r;
    int sum;
    vector vec;

    inline void init() {
        vec.push_back(0);
        vec.push_back(0);
    }

    inline int size() {
        return vec.size() - 2;
    }

    inline void update(int x) {
        vec.push_back(x);
    }

    inline int top() {
        return vec.back();
    }
};

SegmentTree T[N <> 1;
        T[o].l = build(l, mid);
        T[o].r = build(mid + 1, r);
        T[o].sum = T[T[o].l].sum + T[T[o].r].sum;
        int ln = T[T[o].l].size() + 1, rn = T[T[o].r].size() + 1;
        for (int i = 1, j = 1; i <= ln || j <= rn;) {
            if (i  rn || T[T[o].l].vec[i] > T[T[o].r].vec[j]))
                T[o].update(T[T[o].l].vec[i++] + 1);
            else
                T[o].update(T[T[o].r].vec[j++] + 1);
        }
        L[o] = T[T[o].l].size() + 1;
        R[o] = T[o].size() - T[T[o].r].size();
        root[L[o]] = T[o].l, root[R[o]] = T[o].r;
    }
    return o;
}

void modify(int x, int pre, int l, int r, int p, int v) {
    int o = ++cnt;
    T[o].init();
    T[o].sum = T[pre].sum + v;
    T[o].vec = T[pre].vec;
    if (l == r) {
        T[o].update(T[pre].top() + v + 1);
    } else {
        int mid = (l + r) >> 1;
        if (p <= mid)
            T[o].l = ++cnt, modify(x, T[pre].l, l, mid, p, v), T[o].r = T[pre].r;
        else
            T[o].r = ++cnt, modify(x, T[pre].r, mid + 1, r, p, v), T[o].l = T[pre].l;
        T[o].sum = T[T[o].l].sum + T[T[o].r].sum;
        int ln = T[T[o].l].size() + 1, rn = T[T[o].r].size() + 1;
        for (int i = 1, j = 1; i <= ln || j <= rn;) {
            if (i  rn || T[T[o].l].vec[i] > T[T[o].r].vec[j]))
                T[o].update(T[T[o].l].vec[i++] + 1);
            else
                T[o].update(T[T[o].r].vec[j++] + 1);
        }
        L[o] = T[T[o].l].size() + 1;
        R[o] = T[o].size() - T[T[o].r].size();
        root[L[o]] = T[o].l, root[R[o]] = T[o].r;
    }
}

int query(int l, int r, int k) {
    if (l == r)
        return l;
    int sum = 0;
    for (int i = 1; i <= cnt; ++i) {
        if (k = L[i] && k = L[i] + 1)
            sum -= T[T[root[L[i] - 1]].r].sum;
        if (sum >= l)
            return query(l, (l + r) >> 1, i);
    }
    return 0;
}

int main() {
    int n = read(), m = read(), a[N], b[N];
    root[0] = build(1, n);
    for (int i = 1; i <= m; ++i) {
        int op = read(), x = read(), y = read();
        if (op == 1) {
            modify(++n, root[n - 1], 1, n, x, y);
        } else {
            int l = read(), r = read();
            int pos = query((r - l + 2) / 2, 1e9, n);
            int ans = T[root[pos]].vec[r - pos + 1] - 1;
            printf("%d\n", ans);
        }
    }
    return 0;
}

四、线段树合并 永无乡

永无乡提出的线段树合并方法则是以差分的形式进行。我们首先对区间进行差分,然后建立线段树。在合并两棵线段树时,我们先将两棵线段树分别按照深度从大到小遍历,建立新的线段树,并按照从小到大的顺序,将差分的结果进行合并。需要注意的是,在合并中,我们需要判断相邻的两个区间是否可以合并。具体的代码示例如下:

const int N = 4e6 + 5;

struct SegmentTree {
int l, r;
int s, c;
int lc, rc;
} T[N];
int n, m, cnt;

void pushup(int o) {
if (T[o].l == T[o].r) return;
T[o].c = T[T[o].lc].c + T[T[o].rc].c;
if (T[T[o].lc].s == T[T[o].lc].c)
T[o].lc = T[T[o].lc].rc = 0;
if (T[T[o].rc].s == T[T[o].rc].c)
T[o].rc = T[T[o].rc].lc = 0;
}

void build(int &o, int l, int r) {
o = ++cnt;
T[o].l = l, T[o].r = r, T[o].c = T[o].s = 0;
if (l == r) return;
int mid = (l + r) >> 1;
build(T[o].lc, l, mid);
build(T[o].rc, mid + 1, r);
}

void modify(int o, int p, int v) {
if (T[o].l == T[o].r) {
T[o].s += v;
T[o].c = T[o].s;
return;
}
int mid = (T[o].l + T[o].r) >> 1;
if (p <= mid)
modify(T[o].lc, p, v);
else
modify(T[o].rc, p, v);
pushup(o);
}

int merge(int o1, int o2) {
if (!o1 || !o2)
return o1 + o2;
int o = ++cnt;
T[o].l = T[o1].l, T[o].r = T[o1].r;
if (T[o1].l == T[o1].r) {
T[o].c = T[o].s = (T[o1].s || T[o2].s);
return o;
}
T[o].lc = merge(T[o1].lc, T[o2].lc);
T[o].rc = merge(T[o1].rc, T[o2].rc);
pushup(o);
return o;
}

int main() {
n = read();
build(root, 1, n);
for (int i = 1; i <= n; ++i)
modify(root, i, read());
m = read();
while (m--) {
int op = read(), l = read(), r = read();
if (op == 1) {
modify(root, l, r);
}

原创文章,作者:XELKF,如若转载,请注明出处:https://www.506064.com/n/332244.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
XELKFXELKF
上一篇 2025-01-21 17:30
下一篇 2025-01-21 17:30

相关推荐

  • Python官网中文版:解决你的编程问题

    Python是一种高级编程语言,它可以用于Web开发、科学计算、人工智能等领域。Python官网中文版提供了全面的资源和教程,可以帮助你入门学习和进一步提高编程技能。 一、Pyth…

    编程 2025-04-29
  • 如何解决WPS保存提示会导致宏不可用的问题

    如果您使用过WPS,可能会碰到在保存的时候提示“文件中含有宏,保存将导致宏不可用”的问题。这个问题是因为WPS在默认情况下不允许保存带有宏的文件,为了解决这个问题,本篇文章将从多个…

    编程 2025-04-29
  • Java Thread.start() 执行几次的相关问题

    Java多线程编程作为Java开发中的重要内容,自然会有很多相关问题。在本篇文章中,我们将以Java Thread.start() 执行几次为中心,为您介绍这方面的问题及其解决方案…

    编程 2025-04-29
  • Python爬虫乱码问题

    在网络爬虫中,经常会遇到中文乱码问题。虽然Python自带了编码转换功能,但有时候会出现一些比较奇怪的情况。本文章将从多个方面对Python爬虫乱码问题进行详细的阐述,并给出对应的…

    编程 2025-04-29
  • NodeJS 建立TCP连接出现粘包问题

    在TCP/IP协议中,由于TCP是面向字节流的协议,发送方把需要传输的数据流按照MSS(Maximum Segment Size,最大报文段长度)来分割成若干个TCP分节,在接收端…

    编程 2025-04-29
  • 如何解决vuejs应用在nginx非根目录下部署时访问404的问题

    当我们使用Vue.js开发应用时,我们会发现将应用部署在nginx的非根目录下时,访问该应用时会出现404错误。这是因为Vue在刷新页面或者直接访问非根目录的路由时,会认为服务器上…

    编程 2025-04-29
  • 如何解决egalaxtouch设备未找到的问题

    egalaxtouch设备未找到问题通常出现在Windows或Linux操作系统上。如果你遇到了这个问题,不要慌张,下面我们从多个方面进行详细阐述解决方案。 一、检查硬件连接 首先…

    编程 2025-04-29
  • Python折扣问题解决方案

    Python的折扣问题是在计算购物车价值时常见的问题。在计算时,需要将原价和折扣价相加以得出最终的价值。本文将从多个方面介绍Python的折扣问题,并提供相应的解决方案。 一、Py…

    编程 2025-04-28
  • Python存款买房问题

    本文将会从多个方面介绍如何使用Python来解决存款买房问题。 一、计算存款年限和利率 在存款买房过程中,我们需要计算存款年限和存款利率。我们可以使用以下代码来计算存款年限和利率:…

    编程 2025-04-28
  • 如何解决当前包下package引入失败python的问题

    当前包下package引入失败python的问题是在Python编程过程中常见的错误之一。 它表示Python解释器无法在导入程序包时找到指定的Python模块。 正确地说,Pyt…

    编程 2025-04-28

发表回复

登录后才能评论