二维线段树

二维线段树

1.算法分析

对于二维的线段树,有2种写法,分别为四叉树写法和树套树写法:

1.1 四叉树写法

参照一维线段树的写法:每次将区间二分成两个子区间,对应矩阵应对X,Y同时二分,也就是4个子矩阵,即左上、右上、左下,右下四部分,所以要建一颗四叉树,如果当前矩阵为一维时,只需二分成两个子区间。具体写法和一维线段树类似,可以查看代码

1.2 树套树写法

树套树指的是线段树套线段树,现在x轴建立一颗线段树,然后在每个节点上面再建立一颗y轴的线段树。这样无论是查询还是修改,先找到x轴的线段树节点,然后再在y轴的线段树上进行修改或者查找即可。

2.模板

参考:https://blog.csdn.net/u012469987/article/details/47341457

2.1 四叉树写法:单点修改,区间询问

#include <bits/stdc++.h>

using namespace std;

const int MAXN = 1e3 + 13;
int n, m, x, y, L, T, q, a[MAXN][MAXN];
struct tree {
   
    int x1, y1, x2, y2;
    int Max, Min;
} tr[MAXN * MAXN * 4];

int son(int p, int x) {
    return p * 4 - 2 + x; }

void pushup1(int rt) {
   
    tr[rt].Max = max(tr[son(rt, 0)].Max, tr[son(rt, 1)].Max);
    tr[rt].Min = min(tr[son(rt, 0)].Min, tr[son(rt, 1)].Min);
    for (int i = 2; i < 4; ++i) {
   
        tr[rt].Max = max(tr[rt].Max, tr[son(rt, i)].Max);
        tr[rt].Min = min(tr[rt].Min, tr[son(rt, i)].Min);
    }
}

void pushup2(int rt) {
     //二叉树的pushup
    tr[rt].Max = max(tr[son(rt, 0)].Max, tr[son(rt, 1)].Max);
    tr[rt].Min = min(tr[son(rt, 0)].Min, tr[son(rt, 1)].Min);
}

void build(int rt, int X1, int Y1, int X2, int Y2) {
   
    tr[rt].x1 = X1, tr[rt].x2 = X2;
    tr[rt].y1 = Y1, tr[rt].y2 = Y2;
    if (X1 == X2 && Y1 == Y2) {
   
        tr[rt].Max = a[X1][Y1];
        tr[rt].Min = a[X1][Y1];
        return;
    }
    int midx = (X1 + X2) >> 1;
    int midy = (Y1 + Y2) >> 1;
    if (X1 == X2) {
   
        build(son(rt, 0), X1, Y1, X2, midy);
        build(son(rt, 1), X1, midy + 1, X2, Y2);
        pushup2(rt);
    } else if (Y1 == Y2) {
   
        build(son(rt, 0), X1, Y1, midx, Y2);
        build(son(rt, 1), midx + 1, Y1, X2, Y2);
        pushup2(rt);
    } else {
   
        build(son(rt, 0), X1, Y1, midx, midy);
        build(son(rt, 1), midx + 1, Y1, X2, midy);
        build(son(rt, 2), X1, midy + 1, midx, Y2);
        build(son(rt, 3), midx + 1, midy + 1, X2, Y2);
        pushup1(rt);
    }
}

int A, B;
void query(int rt, int X1, int Y1, int X2, int Y2) {
   
    if (tr[rt].x1 > X2 || tr[rt].x2 < X1 || tr[rt].y1 > Y2 || tr[rt].y2 < Y1)
        return;
    if (tr[rt].x1 >= X1 && tr[rt].x2 <= X2 && tr[rt].y1 >= Y1 && tr[rt].y2 <= Y2) {
   
        A = max(A, tr[rt].Max);
        B = min(B, tr[rt].Min);
        return;
    }
    if (tr[rt].x1 == tr[rt].x2 || tr[rt].y1 == tr[rt].y2) {
   
        query(son(rt, 0), X1, Y1, X2, Y2);
        query(son(rt, 1), X1, Y1, X2, Y2);
    } else {
   
        query(son(rt, 0), X1, Y1, X2, Y2);
        query(son(rt, 1), X1, Y1, X2, Y2);
        query(son(rt, 2), X1, Y1, X2, Y2);
        query(son(rt, 3), X1, Y1, X2, Y2);
    }
}

void modify(int rt, int X1, int Y1, int X2, int Y2, int val) {
   
    if (tr[rt].x1 > X2 || tr[rt].x2 < X1 || tr[rt].y1 > Y2 || tr[rt].y2 < Y1)
        return;
    if (tr[rt].x1 >= X1 && tr[rt].x2 <= X2 && tr[rt].y1 >= Y1 && tr[rt].y2 <= Y2) {
   
        tr[rt].Max = val;
        tr[rt].Min = val;
        return;
    }
    if (tr[rt].x1 == tr[rt].x2 || tr[rt].y1 == tr[rt].y2) {
   
        modify(son(rt, 0), X1, Y1, X2, Y2, val);
        modify(son(rt, 1), X1, Y1, X2, Y2, val);
        pushup2(rt);
    } else {
   
        modify(son(rt, 0), X1, Y1, X2, Y2, val);
        modify(son(rt, 1), X1, Y1, X2, Y2, val);
        modify(son(rt, 2), X1, Y1, X2, Y2, val);
        modify(son(rt, 3), X1, Y1, X2, Y2, val);
        pushup1(rt);
    }
}

int main() {
   
    scanf("%d", &T);
    for (int i = 1; i <= T; ++i) {
   
        scanf("%d", &n);
        for (int i = 1; i <= n; ++i)
            for (int j = 1; j <= n; ++j) scanf("%d", &a[i][j]);
        build(1, 1, 1, n, n);  // 建树
        scanf("%d", &q);
        printf("Case #%d:\n", i);
        int X1, X2, Y1, Y2;
        while (q--) {
   
            scanf("%d %d %d", &x, &y, &L);
            L = (L - 1) >> 1;
            X1 = max(1, x - L), Y1 = max(1, y - L);  // 找到边界
            X2 = min(n, x + L), Y2 = min(n, y + L);
            A = 0, B = 1e9 + 19;
            query(1, X1, Y1, X2, Y2);  //区间查询
            modify(1, x, y, x, y, (A + B) >> 1);  // 单点修改
            printf("%d\n", (A + B) >> 1);
        }
    }
    return 0;
}

2.2 树套树写法:单点修改,区间询问

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

typedef long long LL;
using namespace std;

const int MAXN = 1025;

LL sum[MAXN * 4][MAXN * 4];
int n;

void subBuild(int rt, int l, int r, int x) {
   
    sum[x][rt] = 0;
    if (l != r) {
   
        int m = (l + r) >> 1;
        subBuild(rt << 1, l, m, x);
        subBuild(rt << 1 | 1, m + 1, r, x);
    }
}

void build(int rt, int l, int r) {
   
    subBuild(1, 1, n, rt);
    if (l != r) {
   
        int m = (l + r) >> 1;
        build(rt << 1, l, m);
        build(rt << 1 | 1, m + 1, r);
    }
}

void subUpdate(int rt, int l, int r, int x, int Y, int num) {
   
    if (l == r && r == Y) {
   
        sum[x][rt] += num;
    } else {
   
        int m = (l + r) >> 1;
        if (Y <= m) subUpdate(rt << 1, l, m, x, Y, num);
        if (Y > m) subUpdate(rt &
  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值