线段树-zkw、伪动态开点、永久化标记模板

线段树空间

下面“空间”是指实际维护的区间长度所占空间

  • 指针版动态开点由于 C++ 里 struct 的“对齐”操作,会额外占用内存。所以内存非常紧张的时候还是必须牺牲一点效率写数组版动态开点。
  • zkw 线段树由于必须是一颗有 \(2^k\) 个叶节点的满二叉树,所以需要 4 倍空间
  • 普通线段树需要 2 倍空间

指针版伪动态开点 + 永久化标记

区间 max NKOJ2297 数列操作
实际上是静态数组,动态开点只是指用到某个节点才去 p = &[++STCnt]

#include <cstdio>
#include <algorithm>

using namespace std;

typedef long long ll;

const ll INF = 1e18;
const int _N = 110000;

int N, M, STCnt;

struct node {
    ll mx, lazy;
    node *l, *r;
} ST[_N * 2], *Rt;

void build(node *&p, int l, int r)
{
    if (p == NULL) p = &ST[++STCnt];
    if (l == r) {
        scanf("%lld", &p->mx);
        return;
    }
    int mid = (l + r) >> 1;
    build(p->l, l, mid), build(p->r, mid + 1, r);
    p->mx = max(p->l->mx, p->r->mx);
    return;
}

void add(node *&p, int l, int r, int s, int t, ll v)
{
    if (s <= l && r <= t) {
        p->mx += v, p->lazy += v;
        return;
    }
    int mid = (l + r) >> 1;
    if (s <= mid) add(p->l, l, mid, s, t, v);
    if (t > mid) add(p->r, mid + 1, r, s, t, v);
    p->mx = max(p->l->mx, p->r->mx) + p->lazy;
    return;
}

void query(node *&p, int l, int r, int s, int t, ll &v)
{
    if (s <= l && r <= t) {
        v = p->mx;
        return;
    }
    int mid = (l + r) >> 1;
    ll a = -INF, b = -INF;
    if (s <= mid) query(p->l, l, mid, s, t, a);
    if (t > mid) query(p->r, mid + 1, r, s, t, b);
    v = max(a, b) + p->lazy;
    return;
}

int main()
{
    scanf("%d", &N);
    build(Rt, 1, N);
    scanf("%d", &M);
    while (M--) {
        char ins[5];
        int x, y;
        ll z;
        scanf("%s", ins);
        if (ins[1] == 'D') {
            scanf("%d%d%lld", &x, &y, &z);
            add(Rt, 1, N, x, y, z);
        } else {
            scanf("%d%d", &x, &y);
            query(Rt, 1, N, x, y, z);
            printf("%lld\n", z);
        }
    }
    return 0;
}

zkw 线段树

zkw 线段树的区间修改暂时还不会,有点惨呀 OrzOrzOrz 。学是不可能学的,就只有写指针线段树再拼命卡卡常数才能维持得了生活这样子……

NKOJ2742 【线段树】区间极大值1

#include <stdio.h>
#include <algorithm>

using namespace std;

const int INF = 1e9;

int N, M, zkw;
int A[420000];

int main()
{
    scanf("%d%d", &N, &M);
    
    for (zkw = 1; zkw <= N; zkw <<= 1);
    for (int i = 0; i <= N + 1; ++i)
        A[zkw + i] = -INF;
    for (int i = 1; i <= N; ++i)
        scanf("%d", &A[zkw + i]);
    for (int i = zkw - 1; i >= 1; --i)
        A[i] = max(A[i << 1], A[i << 1 | 1]);
    while (M--) {
        int x, y, ans = -INF;
        scanf("%d%d", &x, &y);
        int a = zkw + x - 1, b = zkw + y + 1;
        while (a ^ 1 ^ b) {
            if (a & 1 ^ 1) ans = max(ans, A[a ^ 1]);
            if (b & 1) ans = max(ans, A[b ^ 1]);
            a >>= 1, b >>= 1;
        }
        printf("%d\n", ans);
    }
    return 0;
}

数组版伪动态开点

写 struct 是因为这份代码是当时写完指针版之后改的,太懒,就是不想把 mx 改成数组。

NKOJ2742 【线段树】区间极大值1

#include <stdio.h>
#include <algorithm>

using namespace std;

const int INF = 1e9;

int N, M, Cnt, Rt, L[420000], R[420000];

struct node {
    int mx;
    node()
    {
        mx = -INF;
        return;
    }
} A[420000];

void modify(int &p, int l, int r, int k, int d)
{
    if (!p) p = ++Cnt;
    if (l == r) { A[p].mx = d; return; }
    int mid = (l + r) >> 1;
    if (k <= mid) modify(L[p], l, mid, k, d), A[p].mx = max(A[p].mx, A[L[p]].mx);
    else modify(R[p], mid + 1, r, k, d), A[p].mx = max(A[p].mx, A[R[p]].mx);
    return;
}

int query(int &p, int l, int r, int s, int t)
{
    if (!p) p = ++Cnt;
    if (s <= l && r <= t) return A[p].mx;
    int mid = (l + r) >> 1, tmp = -INF;
    if (s <= mid && t >= l) tmp = max(tmp, query(L[p], l, mid, s, t));
    if (s <= r && t > mid) tmp = max(tmp, query(R[p], mid + 1, r, s, t));
    return tmp;
}

int main()
{
    scanf("%d%d", &N, &M);
    
    for (int t, i = 1; i <= N; ++i) {
        scanf("%d", &t);
        modify(Rt, 1, N, i, t);
    }
    while (M--) {
        int x, y;
        scanf("%d%d", &x, &y);
        printf("%d\n", query(Rt, 1, N, x, y));
    }
    return 0;
}

转载于:https://www.cnblogs.com/ghcred/p/9553488.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值