すのはら荘春原庄的雪

二维线段树

Toretto·2022-05-25 16:46·205 次阅读

二维线段树

关于二维的线段树,好像是有两种方法实现?

一种为每个一维节点做一个线段数,就是树套树,另一种是四叉的一维线段树。本人实属菜鸡,只记录了第一中方法。

第一种方法时间复杂度是log^2,空间复杂度是n*n*log^2,似乎也够用哈😀。

基本思想:

把第二维(即矩阵的y轴)看作一维(x轴)的一个节点,而这个节点又是一个单独的线段树

如图是一个4*4的矩阵,我们可以现在x轴建立一个线段树,然后再在每一个x轴的节点下建立关于y轴的线段树。这样不管是查询还是修改,只需要先找到x轴的线段树节点,再遍历y轴的线段树即可。

 

具体操作

1.建树&单点修改

   x轴方向:
      

void upd1(int l,int r,int o,int x,int y,int v){//x为要更新的一维下标,y为要更新的二维下标
    if(l==r){
        fg=1;//打上标记方便二维线段树确定一维节点
        xo=o;
        upd2(1,n,1,y,v);
        return ;
    }
    int mid=l+((r-l)>>1);
    if(x<=mid)
        upd1(l,mid,o<<1,x,y,v);
    else
        upd1(mid+1,r,o<<1|1,x,y,v);
    fg=0,xo=o;
    upd2(1,n,1,y,v);
}

  y轴方向:

o为二维节点,xo为一维节点,v即想要赋予的值

void upd2(int l,int r,int o,int y,int v){
    if(l==r){//如果找到了二维下标
        if(fg)//如果此时正好找到了一维下标,就赋值
            mi[xo][o]=v,mx[xo][o]=v;
        else{//否则更新一维的情况
            mi[xo][o]=min(mi[xo<<1][o],mi[xo<<1|1][o]);
            mx[xo][o]=max(mx[xo<<1][o],mx[xo<<1|1][o]);
        }
        return ;
    }
    int mid=l+((r-l)>>1);
    if(y<=mid)
        upd2(l,mid,o<<1,y,v);
    else
        upd2(mid+1,r,o<<1|1,y,v);
    mi[xo][o]=min(mi[xo][o<<1],mi[xo][o<<1|1]);//维护二维线段树
    mx[xo][o]=max(mx[xo][o<<1],mx[xo][o<<1|1]);
}

可以发现,相对于一维的线段树,二维线段树不需要重新开一个数组来存初始值(一维的也可以不用)分开来看就是两个基本的一维线段树,只不过相互连接起来罢了。

建树和修改完全相同,每个位置附上想要的值即可。

2.查询

  同样,查询操作也是首先找到x轴的节点,然后深入二维线段树确定所查询的区间。

  mians=inf,mxans=0;
  mians为最小值,mxnas为最大值。
  查询时查询的为左上角x,y到右下角x1,y1的范围矩阵。

  x轴:

void qy1(int l,int r,int o,int lx,int rx,int ly,int ry){
    if(lx<=l&&rx>=r){
        qy2(1,n,1,o,ly,ry);
        return ;
    }
    int mid=l+((r-l)>>1);
    if(lx<=mid)
        qy1(l,mid,o<<1,lx,rx,ly,ry);
    if(rx>mid)
        qy1(mid+1,r,o<<1|1,lx,rx,ly,ry);
}

  y轴:

void qy2(int l,int r,int o,int pre,int ly,int ry){
    if(ly<=l&&ry>=r){
        mians=min(mians,mi[pre][o]);
        mxans=max(mxans,mx[pre][o]);
        return ;
    }
    int mid=l+((r-l)>>1);
    if(ly<=mid)
        qy2(l,mid,o<<1,pre,ly,ry);
    if(ry>mid)
        qy2(mid+1,r,o<<1|1,pre,ly,ry);
}

查询时因为所需范围确定,在y轴线段树中无需更新一维的情况,区间和也同理。

最后附上完整模板:

#include<bits/stdc++.h>
using namespace std;
const int maxn=1500;
const int inf=1e8;
int n,q,mx[maxn][maxn],mi[maxn][maxn];
int fg,xo,mians,mxans;

void upd2(int l,int r,int o,int y,int v){
    if(l==r){//如果找到了二维下标
        if(fg)//如果此时正好找到了一维下标,就赋值
            mi[xo][o]=v,mx[xo][o]=v;
        else{//否则更新一维的情况
            mi[xo][o]=min(mi[xo<<1][o],mi[xo<<1|1][o]);
            mx[xo][o]=max(mx[xo<<1][o],mx[xo<<1|1][o]);
        }
        return ;
    }
    int mid=l+((r-l)>>1);
    if(y<=mid)
        upd2(l,mid,o<<1,y,v);
    else
        upd2(mid+1,r,o<<1|1,y,v);
    mi[xo][o]=min(mi[xo][o<<1],mi[xo][o<<1|1]);//维护二维线段树
    mx[xo][o]=max(mx[xo][o<<1],mx[xo][o<<1|1]);
}

void upd1(int l,int r,int o,int x,int y,int v){//x为要更新的一维下标,y为要更新的二维下标
    if(l==r){
        fg=1;//打上标记方便二维线段树确定一维节点
        xo=o;
        upd2(1,n,1,y,v);
        return ;
    }
    int mid=l+((r-l)>>1);
    if(x<=mid)
        upd1(l,mid,o<<1,x,y,v);
    else
        upd1(mid+1,r,o<<1|1,x,y,v);
    fg=0,xo=o;
    upd2(1,n,1,y,v);
}

void qy2(int l,int r,int o,int pre,int ly,int ry){
    if(ly<=l&&ry>=r){
        mians=min(mians,mi[pre][o]);
        mxans=max(mxans,mx[pre][o]);
        return ;
    }
    int mid=l+((r-l)>>1);
    if(ly<=mid)
        qy2(l,mid,o<<1,pre,ly,ry);
    if(ry>mid)
        qy2(mid+1,r,o<<1|1,pre,ly,ry);
}

void qy1(int l,int r,int o,int lx,int rx,int ly,int ry){
    if(lx<=l&&rx>=r){
        qy2(1,n,1,o,ly,ry);
        return ;
    }
    int mid=l+((r-l)>>1);
    if(lx<=mid)
        qy1(l,mid,o<<1,lx,rx,ly,ry);
    if(rx>mid)
        qy1(mid+1,r,o<<1|1,lx,rx,ly,ry);
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    cin>>n;
    int v;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            cin>>v;
            upd1(1,n,1,i,j,v);  
        }
    }
    cin>>q;
    while(q--){
        string op;
        cin>>op;
        int x,y,x1,y1;
        if(op[0]=='q'){
            cin>>x>>y>>x1>>y1;
            mians=inf;
            mxans=0;
            qy1(1,n,1,x,x1,y,y1);
            cout<<mians<<' '<<mxans<<'\n';
        }
        else{
            cin>>x>>y>>v;
            upd1(1,n,1,x,y,v);
        }
    }
    return 0;
}

 

posted @   cbmango  阅读(205)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
点击右上角即可分享
微信分享提示