用于快速求数字子矩阵的和,更新和查询的时间复杂度都是log(n)*log(m) (n,m分别为两维的大小)。
一个二维树状数组元素的例子如下图:
此题需要注意的两点:1、与一维类似,二维树状数组的两维下标都只能从1开始,所以如果区域的横纵坐标从0开始,那么要人工加1处理。2、sum(x,y)计算的是(1,1)到(x,y)这个子矩阵之间值的和。所以求以(a,b)和(c,d)为矩阵两对角的计算方法要注意将边界值计算进去,即sum(c,d)-sum(c,b-1)-sum(a-1,d)+sum(a-1,b-1)
#include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #define N 1040 #define INF 0x3fffffff using namespace std; int tree[N][N]; int n; int lowbit(int x){ return x&(-x); } void add(int a,int b,int x){ int i,j; for(i = a;i<=n;i+=lowbit(i)) for(j = b;j<=n;j+=lowbit(j)) tree[i][j] += x; } int sum(int a,int b){ int i,j,res=0; for(i = a;i>=1;i-=lowbit(i)) for(j = b;j>=1;j-=lowbit(j)) res += tree[i][j]; return res; } int main(){ int cmd,l,b,r,t,a,y,x; while(scanf("%d",&cmd) && cmd<3){ if(cmd == 0){ scanf("%d",&n); memset(tree,0,sizeof(tree)); }else if(cmd == 1){ scanf("%d %d %d",&x,&y,&a); add(x+1,y+1,a); }else{ scanf("%d %d %d %d",&l,&b,&r,&t); printf("%d\n",sum(r+1,t+1)-sum(l,t+1)-sum(r+1,b)+sum(l,b)); } } return 0; }
2155
题意:N * N 的矩阵,每个元素要么是0,要么是1,开始全0。不断进行两种操作:
1) C x1 y1 x2 y2 表示要将左上角为(x1,y1),右下 角为(x2,y2)的子矩阵里的全部元素都取反(0 变1,1变0) (1<=x1<=x2<=n,1<=y1<=y2<=n)
2) Q x y 查询(x,y)
思路:对四个角进行标记,查询时看该点左方和上方标记的点数之和是奇数还是偶数即可。
#include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <queue> #include <cstdlib> using namespace std; #define clc(s,t) memset(s,t,sizeof(s)) #define INF 0x3fffffff #define N 1005 int tree[N][N]; int n,q,T; int lowbit(int x){ return x&(-x); } void add(int x,int y){ for(int i = x;i<=n;i+=lowbit(i)) for(int j = y;j<=n;j+=lowbit(j)) tree[i][j] += 1; } int sum(int x,int y){ int i,j,res=0; for(i = x;i>=1;i-=lowbit(i)) for(j = y;j>=1;j-=lowbit(j)) res += tree[i][j]; return res; } int main(){ scanf("%d",&T); while(T--){ int a,b,c,d; scanf("%d %d",&n,&q); clc(tree,0); while (q--) { char ch; getchar(); ch = getchar(); if(ch == 'C'){ scanf("%d %d %d %d",&a,&b,&c,&d); add(a,b); add(a,d+1); add(c+1,b); add(c+1,d+1); }else{ scanf("%d %d",&a,&b); printf("%d\n",(sum(a,b))%2); } } putchar('\n'); } return 0; }