回顾一维树状数组
初始化、求和
c1 = a1;
c2 = a1 + a2;
c3 = a3;
c4 = a1 + a2 + a3 + a4;
c5 = a5;
c6 = a5 + a6;
……
C16 = a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9 + a10 + a11 + a12 + 13 + a14 + a15 + a16;
求和:
int sum (int c[], int n)
{
int sum = 0;
while(n > 0)
{
sum += c[n];
n -= lowbit(n);
}
}
解释一下上面:假设n = 16那么c[16]代表的就是前16项的和,而 16 -= lowbit(16) = 0;所以循环结束。
更改数值
int change (int i, int x, int n) // i表示更改的位置, x表示改为的数。
{
while(i <= n)
{
c[i] += x;
i += lowbit(i);
}
}
二维树状数组
提出问题
一个由数字构成的大矩阵,能进行两种操作
1) 对矩阵里的某个数加上一个整数(可正可负)
2) 查询某个子矩阵里所有数字的和,要求对每次查询,输出结果。
二维树状数组介绍
一维数组很容易扩展到2维
假设在2维的情况下原始的数据定义为A[][]
那么树状数组就可以定义为C[x][y] = ΣA[i][j]
其中 x - lowbit(x) < i < x; ··········y - lowbit(y) < j < y;
下面我们举个例子:
假设原始的二维数组是:
A[][]={{a11,a12,a13,a14,a15,a16,a17,a18,a19},
{a21,a22,a23,a24,a25,a26,a27,a28,a29},
{a31,a32,a33,a34,a35,a36,a37,a38,a39},
{a41,a42,a43,a44,a45,a46,a47,a48,a49}};为了好理解我们引入中间数组B[][]
B[1]={a11,a11+a12,a13,a11+a12+a13+a14,a15,a15+a16,…} 这是第一行的一维树状数组
B[2]={a21,a21+a22,a23,a21+a22+a23+a24,a25,a25+a26,…} 这是第二行的一维树状数组
B[3]={a31,a31+a32,a33,a31+a32+a33+a34,a35,a35+a36,…} 这是第三行的一维树状数组
B[4]={a41,a41+a42,a43,a41+a42+a43+a44,a45,a45+a46,…} 这是第四行的一维树状数组现在举例二维树状数组:
以下内容c[1][1] 表示为c11 以此类推
c11 = a11; c12 = a11 + a12 ; c13 = a13 ; c14 = a11 + a12 + a13 +
a14; c15 = a15………………c21 = a11 + a21; c22 = a11 + a12 + a22 + a21; c23 = a13 + a23;
C24=a11+a12+a13+a14+a21+a22+a23+a24c31 = a31; c32= a31 + a32; c33 = a33; c34 = a31 + a32 + a33 + a34;c35
= a35;c41 = a11 + a21 + a31 + a41; c42 = a11 +a12 + a21 + a22 + a31 + a32 +
a41 + a42;
以上找规律:
我们发现C中的第一个数字表示的是树状数组在纵向上的个数, 第二个数字表示在横向上的个数(也就是一维树状数组)
讲到这二维数组的定义基本讲完,我们返回前面的两道例题
求和
int Sum(int i, int j)
{
int sum = 0;
for(int x = i; x > 0; x -= lowbit(x))
{
for(int y = j; y > 0; y -= lowbit(y))
{
sum += C[x][y];
}
}
return sum;
}
和一维的完全一样只是加了一个for循环
改变数值
private void Modify(int i, int j, int n, int m, int delta)
{
A[i][j]+=delta;
for(int x = i; x< n; x += lowbit(x))
for(int y = j; y < m; y += lowbit(y))
{
C[x][y] += delta;
}
}