浅谈二维线段树

一、定义

二维线段树,即用线段树维护一个矩阵

有两种实现方式:

1、原一维线段树的基础上,每一个节点都是一个线段树,代表第二维

下图是一个4*4矩阵

 

2、四分法转化为一维线段树

 

两种方法的空间复杂度都是n*n*log^2

第一种方法单次操作的时间复杂度是log^2,第二种方法最差可以退化到n

一维线段树的标记思想,在第一种方法中,可以用于二维线段树的第二维,不可以用于二维线段树的第一维

第二种方法本质上是四叉的一维线段树,

在此只介绍第一种方法

二、基本操作

1、单点修改+矩阵查询

单次访问一个位置,查询一个矩阵的总访问次数

访问位置(x,y)

所有第一维包含x的都包含位置(x,y),访问次数都要加1

所以从根到找到第一维包含x所经过的所有节点,在里面找到第二维包含y的节点,访问次数加1

sum[i][j] 表示的是这个节点所包含的矩阵的所有位置的访问次数之和

第一维:

void changex(int kx,int l,int r)
{
    changey(kx,1,1,h);
    if(l==r) return;
    int mid=l+r>>1;
    if(x<=mid) changex(kx<<1,l,mid);
    else changex(kx<<1|1,mid+1,r);
}

对于第二维的修改有两种写法

①、从根往下找经过的节点 f访问次数全部+1

因为此时已经确定了第一维包含,从根往下找y,所经过的区间一定包含y

 

void changey(int kx,int ky,int l,int r)
{
    sum[kx][ky]++;
    if(l==r) return;
    int mid=l+r>>1;
    if(y<=mid) changey(kx,ky<<1,l,mid);
    else changey(kx,ky<<1|1,mid+1,r);
}

②、在第二维中找到y,访问次数+1,祖先节点通过左右子节点的合并修改

void changey(int kx,int ky,int l,int r)
{
    if(l==r)
    {
        sum[kx][ky]++;
        return;
    }
    int mid=l+r>>1;
    if(y<=mid) changey(kx,ky<<1,l,mid);
    else changey(kx,ky<<1|1,mid+1,r);
    sum[kx][ky]=sum[kx][ky<<1]+sum[kx][ky<<1|1];
}

 

查询:

查询左上角为(xl,yl),右下角为(xr,yr)的矩阵的所有位置的访问次数之和

void queryy(int kx,int ky,int l,int r)
{
    if(l>=yl && r<=yr)
    {
        cnt+=sum[kx][ky];
        return;
    }
    int mid=l+r>>1;
    if(yl<=mid) queryy(kx,ky<<1,l,mid); if(yr>mid) queryy(kx,ky<<1|1,mid+1,r); } void queryx(int kx,int l,int r) { if(l>=xl && r<=xr) { queryy(kx,1,1,h); return; } int mid=l+r>>1; if(xl<=mid) queryx(kx<<1,l,mid); if(xr>mid) queryx(kx<<1|1,mid+1,r); }

 

 

2、矩阵修改+单点查询

修改

访问左上角为(x1,y1),右下角为(x2,y2)的矩阵

sum[i][j] 表示的是这个节点所代表的矩阵的整体访问次数

即这个矩阵的每一个位置都会有一个sum[i][j]的访问次数

void changey(int kx,int ky,int l,int r)
{
    if(y1<=l&&r<=y2)
    {
        sum[kx][ky]++;
        return;
    }
    int mid=l+r>>1;
    if(y1<=mid) changey(kx,ky<<1,l,mid);
    if(y2>mid) changey(kx,ky<<1|1,mid+1,r);
}
void changex(int kx,int l,int r)
{
    if(x1<=l&&r<=x2)
    {
        changey(kx,1,1,n);
        return;
    }
    int mid=l+r>>1;
    if(x1<=mid) changex(kx<<1,l,mid);
    if(x2>mid) changex(kx<<1|1,mid+1,r);
}

 

查询

查询位置(x,y)的访问次数

因为sum[i][j] 表示的是一个矩阵的整体访问次数,所以找(x,y)经过的所有点都要累计其访问次数

void queryy(int kx,int ky,int l,int r)
{
    ans+=sum[kx][ky];
    if(l==r) return;
    int mid=ly+ry>>1;
    if(y<=mid) queryy(kx,ky<<1,l,mid);
    else queryy(kx,ky<<1|1,mid+1,r);
}
void queryx(int kx,int l,int r)
{
    queryy(kx,1,1,n);
    if(l==r) return;
    int mid=l+r>>1;
    if(x<=mid) queryx(kx<<1,l,mid);
    else queryx(kx<<1|1,mid+1,r);
}

 

这两个sum[][] 含义不同,是为了适应不同的修改方式

一个是单点修改,一个是矩阵修改

而且矩阵修改不带标记下传

转载于:https://www.cnblogs.com/TheRoadToTheGold/p/8151375.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值