浅谈二维线段树

一、定义

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

有两种实现方式:

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

时间: 2024-08-27 14:04:12

浅谈二维线段树的相关文章

浅谈二维线段树的几种不同的写法

目录 参考文献 参考文献 暴力写法 二叉树 四叉树 树套树写法1 参考文献 四叉树 树套树 以及和zhoufangyuan巨佬的激烈♂讨论 参考文献 大家好我口糊大师又回来了. 给你一个\(n*n\)矩阵,然后让你支持两种操作,对子矩阵加值和对子矩阵查和. 暴力写法 对于每一行开一个线段树,然后跑,时间复杂度\(n^2logn\). 优点: 代码较短 较为灵活 缺点: 常数大 容易卡 二叉树 我们对于平面如此处理,一层维护横切,一层竖切. 当然,这个做法也是\(n^2logn\)的,卡法就是任意

浅谈二维中的树状数组与线段树

一般来说,树状数组可以实现的东西线段树均可胜任,实际应用中也是如此.但是在二维中,线段树的操作变得太过复杂,更新子矩阵时第一维的lazy标记更是麻烦到不行. 但是树状数组在某些询问中又无法胜任,如最值等不符合区间减法的询问.此时就需要根据线段树与树状数组的优缺点来选择了. 做一下基本操作的对比,如下图. 因为线段树为自上向下更新,从而可以使用lazy标记使得矩阵的更新变的高校起来,几个不足就是代码长,代码长和代码长. 对于将将矩阵内元素变为某个值,因为树状数组自下向上更新,且要满足区间加法等限制

HDU1832 二维线段树求最值(模板)

Luck and Love Time Limit: 10000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 50 Accepted Submission(s): 20   Problem Description 世界上上最远的距离不是相隔天涯海角而是我在你面前可你却不知道我爱你                ―― 张小娴 前段日子,枫冰叶子给Wiskey做了个征婚启事,聘

ZOJ 2859 二维线段树

思路:自己写的第二发二维线段树1A,哈哈,看来对二维的push操作比较了解了:但是还没遇到在两个线段树中同时进行push操作的,其实这题我是想在x维和y维同时进行push操作的,但是想了好久不会,然后看到这题又给出10秒,然后想想在x维线段直接单点查询肯定也过了,然后在第二维就只有pushup操作,在第一维线段树没有pushup操作.要是在第一维也有pushup操作的话,那就不用单点查询那么慢了.不过也A了,想找题即在二维同时进行pushup和pushdown操作的. #include<iost

[POJ2155] Matrix(二维线段树,树套树)

题目链接:http://poj.org/problem?id=2155 题意:给一个01矩阵,两个操作,翻转:子矩阵里每一个数都由0变1,1变0. 查询:查询某一点是0还是1. 一直以为二维线段树就是开一个线段树数组的我- 这题暴力更新每一个小矩形,翻转就+1,最后看看某点的奇偶. 写屎了,特别注意的是在外层查询的时候要先把当前层的内层query掉,接着再向下扩展.这样外层就不用lazy啦 1 #include <algorithm> 2 #include <iostream> 3

poj1195 Mobile phones 二维线段树入门

二维线段树就是树套树,线段树套线段树... #include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #define REP(i,a,b) for(int i=a;i<=b;i++) #define MS0(a) memset(a,0,sizeof(a)) #define lson l,m,rt<<1 #

UVA 11297 Census ——二维线段树

[题目分析] 二维线段树模板题目. 简直就是无比的暴力.时间复杂度为两个log. 标记的更新方式比较奇特,空间复杂度为N^2. 模板题目. [代码] #include <cstdio> #include <cstring> //#include <cmath> #include <cstdlib> #include <map> #include <set> #include <queue> #include <str

tyvj P1716 - 上帝造题的七分钟 二维树状数组区间查询及修改 二维线段树

P1716 - 上帝造题的七分钟 From Riatre    Normal (OI)总时限:50s    内存限制:128MB    代码长度限制:64KB 背景 Background 裸体就意味着身体. 描述 Description “第一分钟,X说,要有矩阵,于是便有了一个里面写满了0的n×m矩阵.第二分钟,L说,要能修改,于是便有了将左上角为(a,b),右下角为(c,d)的一个矩形区域内的全部数字加上一个值的操作.第三分钟,k说,要能查询,于是便有了求给定矩形区域内的全部数字和的操作.第

POJ 2155 二维线段树

POJ 2155  二维线段树 思路:二维线段树就是每个节点套一棵线段树的树. 刚开始因为题目是求A[I,J],然后在y查询那直接ans^=Map[i][j]的时候没看懂,后面自己把图画出来了才理解. 因为只有0和1,所以可以用异或来搞,而不需要每次都需要修改. #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<map> #incl