二维树状数组

eg:luogu P4514 上帝造题的七分钟

\(s\) 为前缀和数组,\(d\) 为差分数组,\(a\) 为原数组。

则:

\[s_{n,m}=\sum\limits_{i=1}^n\sum\limits_{j=1}^ma_{i,j} \]

有:

\[a_{i,j}=\sum\limits_{k=1}^n\sum\limits_{l=1}^jd_{k,l} \]

所以:

\[s_{n,m}=\sum\limits_{i=1}^n\sum\limits_{j=1}^m\sum\limits_{k=1}^i\sum\limits_{l=1}^jd_{i,j} \]

发现 \(d_{1,1}\) 出现 \(n\times m\) 次,\(d_{1,2}\) 出现 \(n\times (m-1)\) 次,\(d_{2,2}\) 出现 \((n-1)\times (m-1)\) 次,\(\cdots\),总结规律,发现 \(d_{x,y}\) 出现 \((n-x+1)\times (m-y+1)\) 次。

所以原式变为:$$s_{n,m}=\sum\limits_{i=1}^n \sum\limits_{j=1}^m (n-i+1) \times (m-j+1) \times d_{i,j}$$

展开:

\[s_{n,m}=\sum\limits_{i=1}^n \sum\limits_{j=1}^m(nm - nj + n - mi + ij - i + m - j + 1) \]

合并同类项:

\[s_{n,m}=\sum\limits_{i=1}^n \sum\limits_{j=1}^m[nm-m(i - 1) - n(j - 1) + (i - 1)(j - 1)] \]

展开:

\[s_{n,m}=\sum\limits_{i=1}^n \sum\limits_{j=1}^m nm \times d_{i,j}-\sum\limits_{i=1}^n \sum\limits_{j=1}^m m(i-1) \times d_{i,j} - \sum\limits_{i=1}^n \sum\limits_{j=1}^m n(j-1) \times d_{i,j} + \sum\limits_{i=1}^n \sum\limits_{j=1}^m (i-1)(j-1) \times d_{i,j} \]

所以二维树状数组维护 \(d_{i,j},(i-1) \times d_{i,j},(j-1) \times d_{i,j}, (i-1)(j-1) \times d_{i,j}\) 就可以了。

修改的话,二维差分即可。查询同理。因为二维树状数组和一维数状数组一样维护的是前缀和。

#include <bits/stdc++.h>

using namespace std;

const int N = (1 << 11) + 1;

int n, m, f1[N][N], f2[N][N], f3[N][N], f4[N][N];
char c, opt;

int lowbit(int x) {
  return x & -x;
}

void add(int x, int y, int k) {
  for (int i = x; i <= n; i += lowbit(i)) {
    for (int j = y; j <= m; j += lowbit(j)) {
      f1[i][j] += k;
      f2[i][j] += (x - 1) * k;
      f3[i][j] += (y - 1) * k;
      f4[i][j] += (x - 1) * (y - 1) * k;
    }
  }
}

int getsum(int x, int y) {
  int sum = 0;
  for (int i = x; i; i -= lowbit(i)) {
    for (int j = y; j; j -= lowbit(j)) {
      sum += f1[i][j] * x * y;
      sum -= f2[i][j] * y;
      sum -= f3[i][j] * x;
      sum += f4[i][j];
    }
  }
  return sum;
}

int main() {
  cin >> c >> n >> m;
  while (cin >> opt) {
    int a, b, c, d, val;
	if (opt == 'L') {
	  cin >> a >> b >> c >> d >> val;
	  add(a, b, val);
	  add(a, d + 1, -val);
	  add(c + 1, b, -val);		
	  add(c + 1, d + 1, val);
	} else {
	  cin >> a >> b >> c >> d;
	  cout << getsum(c, d) - getsum(a - 1, d) - getsum(c, b - 1) + getsum(a - 1, b - 1) << endl;
	}
  }
  return 0;
}
posted @ 2023-10-25 09:48  ydq1101  阅读(31)  评论(0)    收藏  举报