浅谈二维线段树

一、定义

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

有两种实现方式:

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[][] 含义不同,是为了适应不同的修改方式

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

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

posted @ 2018-01-01 19:47  TRTTG  阅读(7958)  评论(0编辑  收藏  举报