線段樹合併及其相關問題

一、線段樹合併題目

線段樹是一種常用的數據結構,在解決區間查詢、修改問題時非常方便。但是,在實際的問題中,我們常常需要對兩個不同的線段樹進行合併,以便更好地完成某些操作。典型的線段樹合併題目包括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/zh-tw/n/332244.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
XELKF的頭像XELKF
上一篇 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

發表回復

登錄後才能評論