矩阵修改(二维线段树、矩形树)

4 篇文章 0 订阅
1 篇文章 0 订阅

矩阵修改(二维线段树、矩形树)

题目描述

有一个n*m的矩阵(原始矩阵有值),现在可以对矩阵进行k次操作

输入

开始n行,每行m个整数,表示原始矩阵
后k行每行先是一个字符表示操作类型,后跟4个整数,表示范围。若是修改操作还会给出1个整数表示修改值。
操作分为三类:
A x1 y1 x2 y2 val:表示对左上角为x1,y1,右下角为x2,y2的区域加上val
S x1 y1 x2 y2 val:表示对左上角为x1,y1,右下角为x2,y2的区域减去val
Q x1 y1 x2 y2:表示查询左上角为x1,y1,右下角为x2,y2的区域的和

输出

对于每次查询,输出结果。每个结果占一行

数据规模

n,m<=1000,k<=10000

华丽的分界线

引言

其实就是一道二维线段树的裸题,但网上二维线段树的资源好像少的可怜,有的也只是树套树版本的,好像无法满足区间修改和区间查询。而且矩形树相对于树套树来说,在代码性能和写起来的难易程度上来说也要好上不少。主要是好理解,还有一维线段树的flg懒标记在矩形树上同样适用。

Code思路

先声明:这里矩形树的当做完全四叉树来做(方便计算儿子和父亲的下标)

矩形树的代码其实跟一维线段树的写法差不多,只是由二叉变成了四叉而已。

首先,一个节点定义为一个结构体,成员包括:坐标信息x1,y1,x2,y2(表示该节点的区间为以(x1,y1)为左上角(x2,y2)为右下角的矩形区域)和其他含义信息比如区间和sum,懒标记flg,区间最大值max,最小值min…什么的你自己根据需要去定就行了。

结构体代码:

const int MAXN = 1000;
struct node{
    short x1, y1, x2, y2;
    int sum, flg;
}T[MAXN * MAXN * 4];

至于建树操作我们可以模仿,一维线段树的建树操作在x方向和y方向二分,如果x或y方向上长度为1了,那在该方向上就不在进行二分了。如果x,y方向上长度均为一了(即x1=x2且y1=y2),就说明到了树的最底层的单位节点了就可以返回了。

建树代码:

void build(int u, short x1, short x2, short y1, short y2){
    T[u].x1 = x1; T[u].x2 = x2;
    T[u].y1 = y1; T[u].y2 = y2;
    if(x1 == x2 && y1 == y2)
        return;
    short midx = (x1 + x2) >> 1;
    short midy = (y1 + y2) >> 1;
    if(x1 == x2){
        build(son1, x1, x2, y1, midy);
        build(son3, x1, x2, midy + 1, y2);
    }
    else if(y1 == y2){
        build(son1, x1, midx, y1, y2);
        build(son2, midx + 1, x2, y1, y2);
    }
    else{
        build(son1, x1, midx, y1, midy);
        build(son2, midx + 1, x2, y1, midy);
        build(son3, x1, midx, midy + 1, y2);
        build(son4, midx + 1, x2, midy + 1, y2);
    }
}

建树看懂了,这个代码就好写了,插入和查询跟一维线段树基本一样,这里就不单独写了,不懂的话就,看代码吧。

Code

#include<cstdio>
#define son1 (u << 2) - 2
#define son2 (u << 2) - 1
#define son3 (u << 2)
#define son4 (u << 2) | 1
const int MAXN = 1000;
struct node{
    short x1, x2, y1, y2;
    int sum, flg;
}T[MAXN * MAXN * 2];

int mp[MAXN + 5][MAXN + 5];

template <class T>
inline void Read(T &Ret){
    char ch; int flg = 1;
    while(ch = getchar(), ch < '0' || ch > '9')
        if(ch == '-') flg = -1;
    Ret = ch - '0';
    while(ch = getchar(), ch >= '0' && ch <= '9')
        Ret = Ret * 10 + ch - '0';
    ungetc(ch, stdin); Ret *= flg;
}

inline int sq(int u){return (T[u].x2 - T[u].x1 + 1) * (T[u].y2 - T[u].y1 + 1);}

inline void pushdown(int u){
    if(!T[u].flg) return;
    if(T[son1].x1) T[son1].sum += sq(son1) * T[u].flg, T[son1].flg += T[u].flg;
    if(T[son2].x1) T[son2].sum += sq(son2) * T[u].flg, T[son2].flg += T[u].flg;
    if(T[son3].x1) T[son3].sum += sq(son3) * T[u].flg, T[son3].flg += T[u].flg;
    if(T[son4].x1) T[son4].sum += sq(son4) * T[u].flg, T[son4].flg += T[u].flg;
    T[u].flg = 0;
}

inline void pushup(int u){T[u].sum = T[son1].sum + T[son2].sum + T[son3].sum + T[son4].sum;}

void build(int u, short x1, short x2, short y1, short y2){
    T[u].x1 = x1; T[u].x2 = x2;
    T[u].y1 = y1; T[u].y2 = y2;
    if(x1 == x2 && y1 == y2){
        T[u].sum = mp[x1][y1];
        return;
    }
    short midx = (x1 + x2) >> 1;
    short midy = (y1 + y2) >> 1;
    if(x1 == x2) build(son1, x1, x2, y1, midy), build(son3, x1, x2, midy + 1, y2);
    else if(y1 == y2) build(son1, x1, midx, y1, y2), build(son2, midx + 1, x2, y1, y2);
    else{
        build(son1, x1, midx, y1, midy);
        build(son2, midx + 1, x2, y1, midy);
        build(son3, x1, midx, midy + 1, y2);
        build(son4, midx + 1, x2, midy + 1, y2);
    }
    pushup(u);
}

void Insert(int u, short x1, short x2, short y1, short y2, int v){
    if(x2 < T[u].x1 || T[u].x2 < x1) return;
    if(y2 < T[u].y1 || T[u].y2 < y1) return;
    if(x1 <= T[u].x1 && T[u].x2 <= x2 && y1 <= T[u].y1 && T[u].y2 <= y2){
        T[u].sum += sq(u) * v;
        T[u].flg += v;
        return;
    }
    pushdown(u);
    Insert(son1, x1, x2, y1, y2, v);
    Insert(son2, x1, x2, y1, y2, v);
    Insert(son3, x1, x2, y1, y2, v);
    Insert(son4, x1, x2, y1, y2, v);
    pushup(u);
}

int getsum(int u, short x1, short x2, short y1, short y2){
    if(x2 < T[u].x1 || T[u].x2 < x1) return 0;
    if(y2 < T[u].y1 || T[u].y2 < y1) return 0;
    if(x1 <= T[u].x1 && T[u].x2 <= x2 && y1 <= T[u].y1 && T[u].y2 <= y2)
        return T[u].sum;
    pushdown(u);
    int res = 0;
    res += getsum(son1, x1, x2, y1, y2);
    res += getsum(son2, x1, x2, y1, y2);
    res += getsum(son3, x1, x2, y1, y2);
    res += getsum(son4, x1, x2, y1, y2);
    pushup(u);
    return res;
}

int main(){
    short n, m, a, b, c, d, k;
    int v;
    char id[3];
    Read(n); Read(m); Read(k);
    for(int i = 1; i <= n; ++ i)
        for(int j = 1; j <= m; ++ j)
            Read(mp[i][j]);
    build(1, 1, n, 1, m);
    while(k --){
        scanf("%s",id);
        Read(a); Read(b); Read(c); Read(d);
        switch(id[0]){
            case 'A': Read(v), Insert(1, a, c, b, d, v); break;
            case 'S': Read(v), Insert(1, a, c, b, d, -v); break;
            default: printf("%d\n",getsum(1, a, c, b, d));
        }
    }
    return 0;
}

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值