赞
踩
前缀和数组、差分数组、线段树、树状数组均用于处理区间问题,但有不同应用场景
前缀和数组:用于处理 连续多次取区间和 操作的情况,取区间和期间不能对原数组进行区间增减数操作
差分数组:用于处理 多次区间增减数操作,最后读取一次区间和(即修改期间读取不频繁) 操作的情况
线段树:用于处理 多次区间增减数操作,期间多次读取区间和(即修改期间读取频繁) 操作的情况
树状数组:用于处理 多次区间增减数操作,期间多次读取区间和(即修改期间读取频繁) 操作的情况,同线段树的情况、但树状数组更加简单且不支持区间更新,线段树支持区间更新但编码复杂
在学习之前,千万不要忘记何时使用前缀和数组
前缀和数组:用于处理 连续多次取区间和 操作的情况,取区间和期间不能对原数组进行区间增减数操作
具体思想:
假定原数组arr,前缀和数组prefixArr,当前讨论位置为i
那么有prefixArr[i]=arr[i]+arr[i-1]+arr[i-2]+......+arr[0](即i位置及之前所有数累加和==>前缀 和)
由于prefixArr[i-1]=arr[i-1]+arr[i-2]+......+arr[0]
因此化简原式:prefixArr[i]=prefixArr[i-1]+arr[i]
2. 求区间和
设求[i,j](j>i)区间的区间和
即求arr[i]+arr[i+1]+......+arr[j-1]+arr[j]
由于prefixArr[j]=arr[j]+arr[j-1]+arr[j-2]+......arr[0],prefixArr[i-1]=arr[i-1]+arr[i-2]+arr[i-3]+......+arr[0]
因此所求区间和为prefixArr[j]-prefixArr[i-1]
简单来说如下:
红色区域为原数组,黄色区域为所求部分(区间和),那么我们先求蓝色区域与绿色区域(前缀和),再相减就得到了黄色区域
3. 代码如下
-
- public class PrefixArr {
- public static void main(String[] args) {
- //获取数组
- int[] arr = getArr(10);
- for (int i = 0; i < arr.length; i++) {
- System.out.print(arr[i] + " ");
- }
- System.out.println();
- //前缀数组
- int[] prefixArr = new int[arr.length];
- prefixArr[0] = arr[0];
- for (int i = 1; i < arr.length; i++) {
- prefixArr[i] = prefixArr[i - 1] + arr[i];
- }
- //获取[i,j]区间的数
- int i = 3;
- int j = 5;
- int ijNum = prefixArr[j] - prefixArr[i - 1];
- System.out.println("ijNum: " + ijNum);
- }
-
- private static int[] getArr(int len) {
- int[] arr = new int[len];
- for (int i = 0; i < arr.length; i++) {
- arr[i] = (int) (Math.random() * 9 + 1);
- }
- return arr;
- }
- }
在学习之前,千万不要忘记何时使用差分数组
差分数组:用于处理 多次区间增减数操作,最后读取一次区间和(即修改期间读取不频繁) 操作的情况
具体思想:
1. 初始化差分数组
假定原数组arr,差分数组diff,注意差分数组diff的长度要比原数组长度多1(或者在区间修改时进行判断,这点在后面会提到);差分数组所有位置初始为0
2. 接下来进行区间修改
因为差分数组是用于多次区间加减数操作后读取一次的情况,这里进行模拟区间增减数
如果要求在区间[i,j]每个数上增加数value,那么规定修改差分数组diff[i]+value,diff[j+1]-value。从这里可以知道为什么diff数组的长度需要比原数组多1,或者在区间修改的时候进行判断,防止数组越界
3. 通过差分数组获取更新值后的 更新数组
定理:差分数组的前缀和是更新数组,更新数组的含义是在[i,j]区间加value,那么更新数组下标从i到j都为value
证明:
设已有差分数组diff,更新数组update,在区间[i,j]每个数上添加value值,其余位置不更新
根据规定有:diff[i]=diff[i]+value=0+value=value,diff[j+1]=diff[j+1]-value=0-value=-value
那么现在求diff的前缀和数组prefix
prefix[0]=diff[0]=0
prefix[1]=diff[0]+diff[1]=prefix[0]+diff[1]=0
prefix[i]=diff[0]+diff[1]+......+diff[i-1]+diff[i]=value
prefix[i+1]=diff[0]+diff[1]+diff[2]+......+diff[i]+diff[i+1]=prefix[i]+diff[i+1]=value+0=value
......
prefix[j]=prefix[j-1]+diff[j]=value+0=value
prefix[j+1]=prefix[j]+diff[j+1]=value+(-value)=0
所以差分数组前缀和数组就是更新数组update
i位置加value会影响 更新数组 i位置以后的位置加value(由于更新数组是差分数组的前缀和),即蓝色和黄色的区域都加value
j+1位置减value会影响 更新数组 j+1位置以后的位置减value(由于更新数组是差分数组的前缀和),即黄色区域减value
所以在经过修改差分数组diff[i]+value,diff[j+1]-value后再求差分数组前缀和数组就是更新数组
4. 代码如下
-
- public class DiffArr {
- public static void main(String[] args) {
- //获取数组
- int[] arr = getArr(10);
- for (int i = 0; i < arr.length; i++) {
- System.out.print(arr[i] + " ");
- }
- System.out.println();
- //差分数组
- int[] diff = new int[arr.length + 1];
- //区间修改,如在区间[i,j]加v <==>d[L]+v,d[R+1]-v 因此差分数组要多一个数
- addRange(2, 4, diff, 10);
- addRange(1, 9, diff, -5);
- //差分数组的前缀和是更新区域的更新值
- for (int i = 1; i < diff.length; i++) {
- diff[i] = diff[i] + diff[i - 1];
- }
- //更新后的数组是原数组+更新数组
- for (int i = 0; i < arr.length; i++) {
- System.out.print((arr[i]+diff[i]) + " ");
- }
- }
-
- public static void addRange(int i, int j, int[] diff, int value) {
- diff[i] += value;
- diff[j + 1] -= value;
- }
-
- private static int[] getArr(int len) {
- int[] arr = new int[len];
- for (int i = 0; i < arr.length; i++) {
- arr[i] = (int) (Math.random() * 9 + 1);
- }
- return arr;
- }
- }
具体思想:
1. 求二维前缀和
定义:二维前缀和是指(0,0)位置与(i,j)围成矩阵内数的和
假定原二维数组为arr,二维前缀和数组prefixArr,求(i,j)位置的前缀和
prefixArr[i][j]=(arr[0][0]+arr[0][1]+arr[0][2]+......+arr[0][j])+(arr[1][0]+arr[1][1]+arr[1][2]+......+arr[1][j])+......+(arr[i][0]+arr[i][1]+arr[i][2]+......+arr[i][j])
由于
prefixArr[i-1][j] = (arr[0][0]+arr[0][1]+arr[0][2]+......+arr[0][j])+(arr[1][0]+arr[1][1]+arr[1][2]+......+arr[1][j])+......+(arr[i-1][0]+arr[i-1][1]+arr[i-1][2]+......+arr[i-1][j])
prefixArr[i][j-1] = (arr[0][0]+arr[0][1]+arr[0][2]+......+arr[0][j-1])+(arr[1][0]+arr[1][1]+arr[1][2]+......+arr[1][j-1])+......+(arr[i][0]+arr[i][1]+arr[i][2]+......+arr[i][j-1])
prefixArr[i-1][j-1] = (arr[0][0]+arr[0][1]+arr[0][2]+......+arr[0][j-1])+(arr[1][0]+arr[1][1]+arr[1][2]+......+arr[1][j-1])+......+(arr[i-1][0]+arr[i-1][1]+arr[i-1][2]+......+arr[i-1][j-1])
因此化简:prefixArr[i][j]=prefixArr[i-1][j]+prefixArr[i][j-1] -prefixArr[i-1][j-1]+arr[i][j]
prefixArr[i-1][j]=蓝色+黄色
prefixArr[i][j-1]=绿色+黄色
prefixArr[i-1][j-1]=黄色
prefixArr[i][j]=红色+蓝色+黄色+绿色=(红色)+(蓝色+黄色)+(绿色+黄色)-黄色=arr[i][j]+prefixArr[i-1][j]+prefix[i][j-1]-prefix[i-1][j-1]
2. 求区间和 (i,j)->(m,n)的区间和
根据图很容易看出:prefixArr[m][n] - prefixArr[i - 1][n] - prefixArr[m][j - 1] + prefixArr[i - 1][j - 1];
3. 代码如下
-
- public class PrefixArr2Dim {
- public static void main(String[] args) {
- int[][] arr = getArr2Dim(8, 8);
- for (int i = 0; i < arr.length; i++) {
- for (int j = 0; j < arr[i].length; j++) {
- System.out.print(arr[i][j] + " ");
- }
- System.out.println();
- }
- System.out.println("==========================");
- //求前缀和
- int[][] prefixArr = new int[arr.length][arr[0].length];
- prefixArr[0][0] = arr[0][0];
- //第一行与第一列需要特殊处理
- for (int j = 1; j < arr[0].length; j++) {
- prefixArr[0][j] = prefixArr[0][j - 1] + arr[0][j];
- }
- for (int i = 1; i < arr.length; i++) {
- prefixArr[i][0] = prefixArr[i - 1][0] + arr[i][0];
- }
- for (int i = 1; i < arr.length; i++) {
- for (int j = 1; j < arr[i].length; j++) {
- prefixArr[i][j] = prefixArr[i - 1][j] + prefixArr[i][j - 1] - prefixArr[i - 1][j - 1] + arr[i][j];
- }
- }
- for (int i = 0; i < prefixArr.length; i++) {
- for (int j = 0; j < prefixArr[i].length; j++) {
- System.out.print(prefixArr[i][j] + " ");
- }
- System.out.println();
- }
- System.out.println("==========================");
- //求区间和 [i,j]与[m,n]的区间和
- int i = 2, j = 3, m = 7, n = 6;
- int sum = prefixArr[m][n] - prefixArr[i - 1][n] - prefixArr[m][j - 1] + prefixArr[i - 1][j - 1];
- System.out.println(sum);
- }
-
- public static int[][] getArr2Dim(int firstDim, int secondDim) {
- int[][] arr = new int[firstDim][secondDim];
- for (int i = 0; i < arr.length; i++) {
- for (int j = 0; j < arr[i].length; j++) {
- arr[i][j] = (int) (Math.random() * 9 + 1);
- }
- }
- return arr;
- }
- }
1. 首先要理解:以下为diff差分数组
如果在(i,j)位置加value值,那么会影响第四象限的所有位置的值加value,如图:
所以如果现在要在(i,j)->(m,n)矩阵区域加value,如图蓝色区域
(i,j)添加value,会影响 蓝色+橙色+绿色+红色 区域
(i,n+1)添加value,会影响 橙色+红色 区域
(m+1,j)添加value,会影响 绿色+红色 区域
(m+1,n+1)添加value,会影响 红色 区域
所以要只让蓝色区域加value,要在(i,j)位置加value,(i,n+1)位置减value,(m+1,j)位置减value,(m+1,n+1)位置加value
2. 差分数组求前缀和是更新数组
3. 更新数组加上原数组是更新修改后的数组
4.代码
-
- public class DiffArr2Dim {
- public static void main(String[] args) {
- //获取原数组
- int[][] arr = getArr2Dim(7, 7);
- //输出
- for (int i = 0; i < arr.length; i++) {
- for (int j = 0; j < arr[i].length; j++) {
- System.out.print(arr[i][j]+" ");
- }
- System.out.println();
- }
- System.out.println("============================");
- //求差分数组
- int[][] diff = new int[arr.length + 1][arr[0].length + 1];
- addRegion(diff, 0, 0, 2, 3, 10);
- addRegion(diff, 1, 1, 5, 6, -10);
- //求差分数组前缀和
- int[][] prefixArr = new int[arr.length][arr[0].length];
- prefixArr[0][0] = diff[0][0];
- //第一行与第一列特殊处理
- for (int j = 1; j < arr[0].length; j++) {
- prefixArr[0][j] = prefixArr[0][j - 1] + diff[0][j];
- }
- for (int i = 1; i < arr.length; i++) {
- prefixArr[i][0] = prefixArr[i - 1][0] + diff[i][0];
- }
- //其余位置
- for (int i = 1; i < arr.length; i++) {
- for (int j = 1; j < arr[i].length; j++) {
- prefixArr[i][j] = prefixArr[i - 1][j] + prefixArr[i][j - 1] - prefixArr[i - 1][j - 1] + diff[i][j];
- }
- }
- //输出
- for (int i = 0; i < prefixArr.length; i++) {
- for (int j = 0; j < prefixArr[i].length; j++) {
- System.out.print(prefixArr[i][j]+" ");
- }
- System.out.println();
- }
- System.out.println("============================");
- //输出修改后的数组
- for (int i = 0; i < arr.length; i++) {
- for (int j = 0; j < arr[i].length; j++) {
- System.out.print((arr[i][j]+diff[i][j])+" ");
- }
- System.out.println();
- }
- }
-
- private static void addRegion(int[][] diff, int i, int j, int m, int n, int value) {
- diff[i][j] += value;
- diff[i][n + 1] -= value;
- diff[m + 1][j] -= value;
- diff[m + 1][n + 1] += value;
- }
-
- public static int[][] getArr2Dim(int firstDim, int secondDim) {
- int[][] arr = new int[firstDim][secondDim];
- for (int i = 0; i < arr.length; i++) {
- for (int j = 0; j < arr[i].length; j++) {
- arr[i][j] = (int) (Math.random() * 9 + 1);
- }
- }
- return arr;
- }
- }
在学习之前,千万不要忘记何时使用线段树
线段树:用于处理 多次区间增减数操作,期间多次读取区间和(即修改期间读取频繁) 操作的情况
具体思想:
将区间的增减更新任务揽住,不下发
当有新的任务到来,下发一层
由于使用树状区间结构揽住任务,每次任务到来时只需要log(N)的时间复杂度
叶子结点代表原数组arr中的某一索引位置的值,内部节点代表原数组arr中某一范围的区间和
- public static class SegmentTree {
- private int MAXN;//最大长度
- private int[] arr;//存储原数组,但下标从1开始
- private int[] sum;//存储区间和
- private int[] lazy;//存储揽住增加的值
- private int[] change;//存储揽住更新的值
- private boolean[] update;//标记change是否是更新
-
- public SegmentTree(int[] origin) {
- MAXN = origin.length + 1;
- arr = new int[MAXN];
- for (int i = 1; i < MAXN; i++) {
- arr[i] = origin[i - 1];
- sum = new int[MAXN << 2];
- lazy = new int[MAXN << 2];
- change = new int[MAXN << 2];
- update = new boolean[MAXN << 2];
- }
- }
-
- //rt位置的sum(也就是rt节点)=rt左孩子的sum+rt右孩子的sum
- private void pushUp(int rt) {
- sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
- }
-
- // 初始化阶段:把sum数组,填好
- // 代表当前sum的rt位置=arr的l-r范围的和
- public void build(int l, int r, int rt) {
- if (l == r) {
- sum[rt] = arr[l];
- return;
- }
-
- int mid = (l + r) >> 1;
- //构造左
- build(l, mid, rt << 1);
- //构造右
- build(mid + 1, r, rt << 1 | 1);
- //求和
- pushUp(rt);
- }
-
- //在L-R范围上加C
- //l-r,当前rt位置的格子代表的范围
- public void add(
- int L, int R, int C,
- int l, int r,
- int rt
- ) {
- if (L <= l && R >= r) {
- sum[rt] += C * (r - l + 1);
- lazy[rt] += C;
- return;
- }
- //任务没有全包
- int mid = (l + r) >> 1;
- pushDown(rt, mid - l + 1, r - mid);
- //需要发给左边再发
- if (L <= mid) {
- add(L, R, C, l, mid, rt << 1);
- }
- //需要发给右边再发
- if (R > mid) {
- add(L, R, C, mid + 1, r, rt << 1 | 1);
- }
- //左右都更新好,现在更新当前
- pushUp(rt);
- }
-
- public void update(
- int L, int R, int C,
- int l, int r, int rt
- ) {
- if (L <= l && R >= r) {
- update[rt] = true;
- change[rt] = C;
- sum[rt] = C * (r - l + 1);
- lazy[rt] = 0;
- return;
- }
-
- //揽不住
- int mid = (l + r) >> 1;
- pushDown(rt, mid - l + 1, r - mid);
- //需要发给左边再发
- if (L <= mid) {
- update(L, R, C, l, mid, rt << 1);
- }
- //需要发给右边再发
- if (R > mid) {
- update(L, R, C, mid + 1, r, rt << 1 | 1);
- }
- //左右都更新好,现在更新当前
- pushUp(rt);
- }
-
- //ln->左孩子范围的数量,rn->右孩子范围的数量
- private void pushDown(int rt, int ln, int rn) {
- if (update[rt]) {
- update[rt << 1] = true;
- update[rt << 1 | 1] = true;
- change[rt << 1] = change[rt];
- change[rt << 1 | 1] = change[rt];
- lazy[rt << 1] = 0;
- lazy[rt << 1 | 1] = 0;
- sum[rt << 1] = change[rt] * ln;
- sum[rt << 1 | 1] = change[rt] * rn;
- update[rt] = false;
- }
- if (lazy[rt] != 0) {
- lazy[rt << 1] += lazy[rt];
- sum[rt << 1] += lazy[rt] * ln;
-
- lazy[rt << 1 | 1] += lazy[rt];
- sum[rt << 1 | 1] += lazy[rt] * rn;
-
- lazy[rt] = 0;
- }
- }
-
- public long query(int L, int R, int l, int r, int rt) {
- if (L <= l && R >= r) {
- return sum[rt];
- }
- int mid = (l + r) >> 1;
- pushDown(rt, mid - l + 1, r - mid);
- long ans = 0;
- if (L <= mid) {
- ans += query(L, R, l, mid, rt << 1);
- }
- if (R > mid) {
- ans += query(L, R, mid + 1, r, rt << 1 | 1);
- }
- return ans;
- }
- }
- #include <iostream>
- using namespace std;
-
- class SegmentTree
- {
- public:
- int MAXN;
- int *arr;
- int *sum;
- int *lazy;
- int *change;
- bool *update;
-
- SegmentTree(int *origin, int n)
- {
- int MAXN = n + 1;
- this->arr = new int[MAXN];
- for (int i = 1; i < MAXN; i++)
- {
- this->arr[i] = origin[i - 1];
- }
-
- this->sum = new int[MAXN];
- this->lazy = new int[MAXN];
- this->change = new int[MAXN];
- this->update = new bool[MAXN];
-
- for (int i = 0; i < MAXN; i++)
- {
- this->sum[i] = 0;
- this->lazy[i] = 0;
- this->change[i] = 0;
- this->update[i] = false;
- }
- }
-
- // 初始化
- void build(int l, int r, int rt)
- {
- if (l >= r)
- {
- this->sum[rt] = this->arr[l];
- return;
- }
-
- int mid = (l + r) >> 1;
- build(l, mid, rt << 1);
- build(mid + 1, r, rt << 1 | 1);
-
- pushUp(rt);
- }
-
- // add
- void add_f(
- int L, int R, int C,
- int l, int r,
- int rt)
- {
- if (L <= l && R >= r)
- {
- this->sum[rt] += (r - l + 1) * C;
- this->lazy[rt] += C;
- return;
- }
-
- int mid = (l + r) >> 1;
- pushDown(rt, mid - l + 1, r - mid); // 下发任务
-
- if (L <= mid)
- {
- add_f(L, R, C, l, mid, rt << 1);
- }
- if (R > mid)
- {
- add_f(L, R, C, mid + 1, r, rt << 1 | 1);
- }
-
- pushUp(rt);
- }
-
- // update
- void update_f(
- int L, int R, int C,
- int l, int r,
- int rt)
- {
- if (L <= l && R >= r)
- {
- this->update[rt] = true;
- this->change[rt] = C;
- this->sum[rt] = C * (r - l + 1);
- this->change[rt] = 0;
- return;
- }
-
- int mid = (l + r) >> 1;
- pushDown(rt, mid - l + 1, r - mid);
- if (L <= mid)
- {
- update_f(L, R, C, l, mid, rt << 1);
- }
- if (R > mid)
- {
- update_f(L, R, C, mid + 1, r, rt << 1 | 1);
- }
- }
-
- // query
- long query(
- int L, int R, int C,
- int l, int r,
- int rt)
- {
- if (L <= l && R >= r)
- {
- return this->sum[rt];
- }
-
- int mid = (l + r) >> 1;
- pushDown(rt, mid - l + 1, r - mid);
-
- long ans = 0;
- if (L <= mid)
- {
- ans += query(L, R, C, l, mid, rt << 1);
- }
- if (R > mid)
- {
- ans += query(L, R, C, mid + 1, r, rt << 1 | 1);
- }
-
- // pushUp(rt);
- return ans;
- }
-
- void pushDown(int rt, int ln, int rn)
- {
- if (this->update[rt])
- {
- this->update[rt << 1] = true;
- this->update[rt << 1 | 1] = true;
-
- this->change[rt << 1] = this->change[rt];
- this->change[rt << 1 | 1] = this->change[rt];
-
- this->sum[rt << 1] = ln * this->change[rt];
- this->sum[rt << 1 | 1] = rn * this->change[rt];
-
- this->lazy[rt << 1] = 0;
- this->lazy[rt << 1 | 1] = 0;
-
- this->update[rt] = false;
- }
- if (this->lazy[rt] != 0)
- {
- this->lazy[rt << 1] += this->lazy[rt];
- this->sum[rt << 1] += this->lazy[rt] * ln;
- this->lazy[rt << 1 | 1] += this->lazy[rt];
- this->sum[rt << 1 | 1] += this->lazy[rt] * rn;
- }
- }
-
- void pushUp(int rt)
- {
- this->sum[rt] = this->sum[rt << 1] + this->sum[rt << 1 | 1];
- }
- };
-
- int main()
- {
- return 0;
- }
在学习之前千万不要忘记何时使用树状数组
用于处理 单点增减操作,期间多次查询 操作的情况,不如线段树强,但非常容易改造为一维、二维、三维的结构
具体思想:
1. tree数组i位置管理origin数组的部分和
如:
tree[1] = origin[1]
tree[2] = orgin[1]+origin[2]
tree[3] = origin[3]
tree[4] = origin[1]+origin[2]+orgin[3]+origin[4]
tree[5] = origin[5]
...
tree[8] = origin[1]+...+orgin[8]
....
具体算法:
tree[index]管理origin[index二进制表示减最后一个1再加1--index二进制]
如:
Index = 6 => 0110,因此管理origin[0101]到origin[0110]位置的数和
2. 现求origin的index位置到1位置的数和(也就是index位置的前缀和)
(1)index求出二进制xxxxxxx
(2)每次取tree[xxxxxxx]
(3)xxxxxxx减去最后一个1,循环2、3步骤
证明:现求7位置的前缀和
7= 0111,tree的0111位置管理origin的0111-0111的数=>tree[0111]
减去最后一个1=>0110,tree的0110位置管理origin的0101-0110的数=>tree[0110]
减去最后一个1=>0100,tree的0100位置管理origin的0001-0100的数=>tree[0100]
tree[0111]+tree[0110]+tree[0100]=>前缀和,得证
3. 若现在对origin的index位置加d,那么会影响tree的哪些位置
(1)index求出二进制xxxxxxx
(2)tree[xxxxxxx]+d
(3)xxxxxxx加上最后一个1,循环2、3步骤
一维:
- public static class IndexTree {
- private int N;
- private int[] tree;
-
- public IndexTree(int size,int[] origin) {
- this.N = size;
- this.tree = new int[N + 1];//树状数组下标从1开始
- }
-
- //每次去掉最后一个一
- public int sum(int index) {
- int ret = 0;
- while (index > 0) {
- ret += tree[index];
- index -= (index) & (-index);
- }
- return ret;
- }
-
- //每次加上最后一个一
- public void add(int index, int d) {
- while (index <= N) {
- this.tree[index] += d;
- index += index & (-index);
- }
- }
- }
- class IndexTree
- {
- public:
- int N;
- int *tree;
-
- IndexTree(int size, int *origin)
- {
- this->N = size;
- this->tree = new int[N + 1];
- }
-
- // sum
- int sum(int index)
- {
- int ans = 0;
- while (index > 0)
- {
- ans += tree[index];
- index -= (index) & (-index);
- }
- return ans;
- }
-
- void add(int index, int d)
- {
- while (index <= N)
- {
- tree[index] += d;
- index += index & (-index);
- }
- }
- };
二维:
- public static class IndexTree2D {
- int N;
- int M;
- int[][] tree;
- int[][] nums;
-
- IndexTree2D(int[][] matrix) {
- N = matrix.length;
- M = matrix[0].length;
- tree = new int[N + 1][M + 1];
- nums = new int[N][M];
-
- for (int i = 0; i < N; i++) {
- for (int j = 0; j < M; j++) {
- update(i, j, matrix[i][j]);
- }
- }
- }
-
- private int sum(int row, int col) {
- int sum = 0;
- for (int i = row + 1; i > 0; i -= i & -i) {
- for (int j = col + 1; j > 0; j -= j & -j) {
- sum += tree[i][j];
- }
- }
- return sum;
- }
-
- public void update(int row, int col, int val) {
- if (N == 0 || M == 0) {
- return;
- }
- int add = val - nums[row][col];
- nums[row][col] = val;
-
- for (int i = row + 1; i <= N; i += i & -i) {
- for (int j = col + 1; j <= M; j += j & -j) {
- tree[i][j] += add;
- }
- }
- }
-
- //从(row1,col1)->(row2,col2)
- int queryRange(int row1, int col1, int row2, int col2) {
- return sum(row2, col2) - sum(row1 - 1, col2) - sum(row2, col1 - 1) + sum(row1 - 1, col1 - 1);
- }
- }
- class IndexTree2D
- {
- public:
- int N;
- int M;
- int **tree;
- int **nums;
-
- IndexTree2D(int **matrix, int N, int M)
- {
- this->N = N;
- this->M = M;
- this->tree = new int *[N + 1];
- this->nums = new int *[N];
-
- for (int i = 0; i < N; i++)
- {
- this->tree[i + 1] = new int[M + 1];
- this->nums[i] = new int[M];
- }
-
- for (int i = 0; i < N; i++)
- {
- for (int j = 0; j < M; j++)
- {
- this->tree[i + 1][j + 1] = 0;
- this->nums[i][j] = 0;
- }
- }
-
- for (int i = 0; i < N; i++)
- {
- for (int j = 0; j < M; j++)
- {
- update(i, j, matrix[i][j]);
- }
- }
- }
-
- void update(int row, int col, int val)
- {
- if (N == 0 || M == 0)
- return;
-
- int add = val - nums[row][col];
- nums[row][col] = val;
-
- for (int i = row + 1; i <= N; i += i & -i)
- {
- for (int j = col + 1; j <= M; j += j & -j)
- {
- tree[i][j] += add;
- }
- }
- }
-
- int sum(int row, int col)
- {
- int ans = 0;
- for (int i = row + 1; i > 0; i -= i & -i)
- {
- for (int j = col + 1; j > 0; j -= j & -j)
- {
- ans += tree[i][j];
- }
- }
- return ans;
- }
- };
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。