二维树状数组——SuperBrother打鼹鼠(Vijos1512)

  树状数组(BIT)是一个查询和修改复杂度都为log(n)的数据结构,主要用于查询任意两位之间的所有元素之和,其编程简单,很容易被实现。而且可以很容易地扩展到二维。让我们来看一道很裸的二维树状数组题:

在一个“打鼹鼠”的游戏中,鼹鼠会不时地从洞中钻出来,不过不会从洞口钻进去(鼹鼠真胆大……)。洞口都在一个大小为n(n<=1024)的正方形中。这个正方形在一个平面直角坐标系中,左下角为(0,0),右上角为(n-1,n-1)。洞口所在的位置都是整点,就是横纵坐标都为整数的点。而SuperBrother也不时地会想知道某一个范围的鼹鼠总数。这就是你的任务。

  每个输入文件有多行。
    第一行,一个数n,表示鼹鼠的范围。
    以后每一行开头都有一个数m,表示不同的操作:
      m=1,那么后面跟着3个数x,y,k(0<=x,y<n),表示在点(x,y)处新出现了k只鼹鼠;
      m=2,那么后面跟着4个数x1,y1,x2,y2(0<=x1<=x2<n,0<=y1<=y2<n),表示询问矩形(x1,y1)-(x2,y2)内的鼹鼠数量;
      m=3,表示老师来了,不能玩了。保证这个数会在输入的最后一行。

    询问数不会超过10000,鼹鼠数不会超过maxlongint。

  把这个问题简单抽象一下。就是:

  • 输入1时,对一个二维矩阵中某个位置上加上一个数k。
  • 输入2时,求出矩阵中[(x1,y1);(x2,y2)]的子矩阵中,元素的总大小并输出。
  • 输入3时,退出。

  这个问题,输入数据规模可能很大(虽然实际上并非这样,出题人懒到随机生成数据)。而且是动态修改,显然不能使用前缀和,所以,树状数组就是我们的首选。而然,树状数组本来是一维的,如何把它推广到二维去呢。其实很简单,其方法类似与先生成没一行原数组的一维树状数组,再把一个个一维树状数组组合成二维的其对应关系为:

  C[1][1]=a[1][1],C[1][2]=a[1][1]+a[1][2],C[1][3]=a[1][3],C[1][4]=a[1][1]+a[1][2]+a[1][3]+a[1][4],c[1][5]=a[1][5],C[1][6]=a[1][5]+a[1][6],...

  C[2][1]=a[1][1]+a[2][1],C[2][2]=a[1][1]+a[1][2]+a[2][1]+a[2][2],C[2][3]=a[1][3]+a[2][3],C[2][4]=a[1][1]+a[1][2]+a[1][3]+a[1][4]+a[2][1]+a[2][2]+a[2][3]+a[2][4], C[2][5]=a[1][5]+a[2][5],C[2][6]=a[1][5]+a[1][6]+a[2][5]+a[2][6],...

  C[3][1]=a[3][1],C[3][2]=a[3][1]+a[3][2],C[3][3]=a[3][3],C[3][4]=a[3][1]+a[3][2]+a[3][3]+a[3][4],C[3][5]=a[3][5],C[3][6]=a[3][5]+a[3][6],...

  C[4][1]=a[1][1]+a[2][1]+a[3][1]+a[4][1],C[4][2]=a[1][1]+a[1][2]+a[2][1]+a[2][2]+a[3][1]+a[3][2]+a[4][1]+a[4][2],C[4][3]=a[1][3]+a[2][3]+a[3][3]+a[4][3],...(太多了,我就写到3吧)

  ……

  通过观察能发现,第一行是本身,第二行是第一行加上其本身,第三行是本身,第四行是第一、二行加上其本身。这和一维的树状数组是一摸一样的。所以,我们很容易就可以写出修改、查询 函数。

void modfily(int x,int y,int data){
    x+=1;y+=1;
    for (int i=x;i<=n;i+=lowbit(i))
        for (int j=y;j<=n;j+=lowbit(j))
            c[i][j]+=data;
}
int sum(int x,int y){
    x+=1;y+=1;
    int result=0;
    for (int i=x;i>0;i-=lowbit(i))
        for (int j=y;j>0;j-=lowbit(j))
            result+=c[i][j];
    return result;
}

  这就是二维树状数组的核心了,代码和一维的相仿,异常简单。大家可能有疑问,为什么i,j要加1。其实这是我被坑以后的领悟,如果i,j=0的话,lowbit也永远为0,程序会陷入死循环,直接TLE。从这两个函数我们也可以看出,二维树状数组的查询、修改时间复杂度为log(n)²。

  数据结构部分解决了,怎么求一个子矩阵中的数的和呢?画张图我们就可以求出,公式为:sum(x2, y2) - sum(x1-1, y2) - sum(x2, y1-1) + sum(x1-1, y1-1)

  顺便附上这道例题的AC code:

#include <cstdio>
#include <cstdlib>
using namespace std;

int m=0,n,x,y,k,x1,y1,c[1026][1026];
void modfily(int,int,int);
int sum(int,int);
inline int lowbit(int x){
    return x&(-x);
}

int main(void){
    scanf("%d",&n);
    for (int i=0;i<n;++i)
        for (int j=0;j<n;++j)   c[i][j]=0;
    while (m!=3){
        scanf("%d",&m);
        if (m==1){
            scanf("%d%d%d",&x,&y,&k);
            modfily(x,y,k);
        }
        if (m==2){
            scanf("%d%d%d%d",&x,&y,&x1,&y1);
            printf("%d\n",abs(sum(x1,y1)-sum(x-1,y1)-sum(x1,y-1)+sum(x-1,y-1)));
        }
    }
    return 0;
}
void modfily(int x,int y,int data){
    x+=1;y+=1;
    for (int i=x;i<=n;i+=lowbit(i))
        for (int j=y;j<=n;j+=lowbit(j))
            c[i][j]+=data;
}
int sum(int x,int y){
    x+=1;y+=1;
    int result=0;
    for (int i=x;i>0;i-=lowbit(i))
        for (int j=y;j>0;j-=lowbit(j))
            result+=c[i][j];
    return result;
}

  

时间: 2024-10-08 06:35:40

二维树状数组——SuperBrother打鼹鼠(Vijos1512)的相关文章

二维树状数组复习—— SuperBrother打鼹鼠

在这个“打鼹鼠”的游戏中,鼹鼠会不时地从洞中钻出来,不过不会从洞口钻进去(鼹鼠真胆大……).洞口都在一个大小为n(n< =1024)的正方形中.这个正方形在一个平面直角坐标系中,左下角为(0,0),右上角为(n-1,n-1).洞口所在的位置都是整点,就是横纵坐标都为整数的点.而SuperBrother也不时地会想知道某一个范围的鼹鼠总数.这就是你的任务. 输入 每个输入文件有多行. 第一行,一个数n,表示鼹鼠的范围. 以后每一行开头都有一个数m,表示不同的操作: m=1,那么后面跟着3个数x,y

YBT1540打鼹鼠_二维树状数组

1540:打鼹鼠_二维树状数组 时间限制: 1000 ms         内存限制: 524288 KB [题目描述] 这是一道模板题. 给出一个 n×m 的零矩阵 A,你需要完成如下操作: 1 x y k:表示元素 Ax,y自增 k: 2 a b c d:表示询问左上角为 (a,b),右下角为 (c,d) 的子矩阵内所有数的和. [输入] 输入的第一行有两个正整数 n,m: 接下来若干行,每行一个操作,直到文件结束. [输出] 对于每个 2 操作,输出一个整数,表示对于这个操作的回答. [输

【二维树状数组】See you~

https://www.bnuoj.com/v3/contest_show.php?cid=9148#problem/F [题意] 给定一个矩阵,每个格子的初始值为1.现在可以对矩阵有四种操作: A x y n1 :给格点(x,y)的值加n1 D x y n1: 给格点(x,y)的值减n1,如果现在格点的值不够n1,把格点置0 M x1 y1 x2 y2:(x1,y1)移动给(x2,y2)n1个 S x1 y1 x2 y2 查询子矩阵的和 [思路] 当然是二维树状数组 但是一定要注意:lowbi

POJ 1195 Mobile phones(二维树状数组)

题目链接:POJ 1195 题意: 给出一个S*S的矩阵(行.列号从1开始),每个元素初始值为0,有两种操作:一种是第X行第Y列元素值加A:另一种是查询给定范围矩阵的所有元素之和(L<=X<=R,B<=Y<=T). 分析: 查询给定范围矩阵的所有元素之和是二维区间和,可以转换为二维前缀和求值.类比一维前缀和求法,二维区间和S(L, B, R, T) = S(1, 1, R, T) - S(1 ,1, L-1, T) - S(1, 1, R, B-1) + S(1, 1, L-1,

POJ 2155 Matrix(二维树状数组,绝对具体)

Matrix Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 20599   Accepted: 7673 Description Given an N*N matrix A, whose elements are either 0 or 1. A[i, j] means the number in the i-th row and j-th column. Initially we have A[i, j] = 0 (1

HDU 5465 Clarke and puzzle Nim游戏+二维树状数组

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5465 Clarke and puzzle Accepts: 42 Submissions: 269 Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others) 问题描述 克拉克是一名人格分裂患者.某一天,有两个克拉克(aa和bb)在玩一个方格游戏. 这个方格是一个n*mn∗m的矩阵,每个格子里有一

HDOJ 4456 Crowd 离散化+二维树状数组

将坐标旋转45度就可以得到正方形,可以用二维树状数组求解... 为了节省内存,提前将树状数组中会被更新的点全都存下来,并离散化 Crowd Time Limit: 5000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 1199    Accepted Submission(s): 282 Problem Description City F in the south

POJ2029:Get Many Persimmon Trees(二维树状数组)

Description Seiji Hayashi had been a professor of the Nisshinkan Samurai School in the domain of Aizu for a long time in the 18th century. In order to reward him for his meritorious career in education, Katanobu Matsudaira, the lord of the domain of

POJ1195 Mobile phones 【二维树状数组】

Mobile phones Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 14288   Accepted: 6642 Description Suppose that the fourth generation mobile phone base stations in the Tampere area operate as follows. The area is divided into squares. The