線段樹分裂/合併模板

模板鏈接
最早從zx神仙口中得知這個玩意,但是一直沒學,現在學一下再說,發現不是什麼神仙東西,說實話有點小失落.按照區間分裂可以在O(logn)O(logn)內完成,不過,合併操作需要枚舉線段樹的每個點進行合併,效率不是太高,數據量1e3差不多就是極限了.且應用面不是很廣.
在這裏插入圖片描述
洛谷所給的模板是多個權值線段樹的維護操作.
下面是模板代碼:

#include <iostream>
#include <cstring>
#include <string>
#include <vector>
#include <queue>
#include <algorithm>
#include <cstdio>
#include <cmath>
#define ll long long
using namespace std;
const int N = 2e6+5;
int n, m, tot, cnt, seq = 1;
int bac[N<<5],  rt[N];
struct Node
{
    int l, r;
    ll val;
}tr[N<<5];
inline int newnod() {return (cnt?bac[cnt--]:++tot);}
inline void del(int p) {bac[++cnt] = p; tr[p].l = tr[p].r = tr[p].val = 0;}
void change(int &p, int l, int r, int pos, int v)
{
    if (!p) {p=newnod();}
    tr[p].val += v;
    if (l == r) return;
    int mid = (l + r) >> 1;
    if (pos <= mid) change(tr[p].l, l, mid, pos, v);
    else change(tr[p].r, mid+1, r, pos, v);
}
ll ask(int p, int l, int r, int xl, int xr)
{
    if (xr < l || r < xl) return 0;
    if (xl <= l && r <= xr) return tr[p].val;
    int mid = (l + r) >> 1;
    return ask(tr[p].l, l, mid, xl, xr) + ask(tr[p].r, mid+1, r, xl, xr);
}
int grank(int p, int l, int r, int k)
{
    if (l == r) return l;
    int mid = (l + r) >> 1;
    if (tr[tr[p].l].val >= k) return grank(tr[p].l, l, mid, k);
    else return grank(tr[p].r, mid+1, r, k - tr[tr[p].l].val);
}
int merge(int x, int y)
{
    if (!x || !y) return x + y;
    tr[x].val += tr[y].val;
    tr[x].l = merge(tr[x].l, tr[y].l);
    tr[x].r = merge(tr[x].r, tr[y].r);
    del(y);
    return x;
}
void split(int x, int &y, ll k)
{
    y = newnod();
    ll v = tr[tr[x].l].val;
    if (k > v) split(tr[x].r, tr[y].r, k-v);
    else swap(tr[x].r, tr[y].r);
    if (k < v) split(tr[x].l, tr[y].l, k);
    tr[y].val = tr[x].val - k;
    tr[x].val = k;
}
int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++)
    {
        int x;
        scanf("%d", &x), change(rt[1], 1, n, i, x);
    }
    while(m--)
    {
        int op; scanf("%d", &op);
        int x, y, z;
        if (op == 0)
        {
            scanf("%d%d%d", &x, &y, &z);
            ll k1 = ask(rt[x], 1, n, 1, z), k2 = ask(rt[x], 1, n, y, z);
            int te = 0;
            split(rt[x], rt[++seq], k1-k2);
            split(rt[seq], te, k2);
            rt[x] = merge(rt[x], te);
        }
        else if (op == 1)
        {
            scanf("%d%d", &x, &y);
            rt[x] = merge(rt[x], rt[y]);
        }
        else if (op == 2)
        {
            scanf("%d%d%d", &x, &y, &z);
            change(rt[x], 1, n, z, y);
        }
        else if (op == 3)
        {
            scanf("%d%d%d", &x, &y, &z);
            printf("%lld\n", ask(rt[x], 1, n, y, z));
        }
        else if (op == 4)
        {
            scanf("%d%d", &x, &y);
            if (tr[rt[x]].val < y) puts("-1");
            else printf("%d\n", grank(rt[x], 1, n, y));
        }
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章