Loading

「学习笔记」二维树状数组

本文算是水文......
二维树状数组解释起来太麻烦了,但写法和一维几乎差不多,就是加了一层循环,So,这篇文章解释会很少几乎没有
update 2022.11.4 我又回来补充点东西
单点修改,区间查询:
这个和一维的相差不大,真的只是多了层循环

#include<bits/stdc++.h>
#define ll long long
#define rll register long long
using namespace std;
ll n,m,p,x,y,z,w;
ll s[4500][4500];
void add(ll,ll,ll);
ll sum(ll,ll);
int main()
{
	scanf("%lld%lld",&n,&m);
	while((scanf("%lld",&p))!=EOF)
	{
		if(p==1)
		{
			scanf("%lld%lld%lld",&x,&y,&z);
			add(x,y,z);
		}
		else
		{
			scanf("%lld%lld%lld%lld",&w,&x,&y,&z);
			printf("%lld\n",sum(y,z)-sum(w-1,z)-sum(y,x-1)+sum(w-1,x-1));
		}
	}
	return 0;
}
void add(ll a,ll b,ll c)
{
	for(rll i=a;i<=n;i+=(i&(-i)))
	{
		for(rll j=b;j<=m;j+=(j&(-j)))
		{
			s[i][j]+=c;
		}
	}
}
ll sum(ll a,ll b)
{
	ll ans=0;
	for(rll i=a;i;i-=(i&(-i)))
	{
		for(rll j=b;j;j-=(j&(-j)))
		{
			ans+=s[i][j];
		}
	}
	return ans;
}

区间修改,单点查询:
区间修改还是差分的思想,只不过这个差分是二维的
设差分数组为 \(d(i, j)\),原数组为 \(a(i, j)\),差分数组的前缀和为 \(sum(i, j)\)
根据差分与前缀和的关系,我们可以知道 \(a(i, j) = sum(i, j - 1) + sum(i - 1, j) - sum(i - 1, j - 1) + d(i, j)\)
再推下去 \(d(i, j) = a(i, j) - sum(i, j - 1) - sum(i - 1, j) + sum(i - 1, j - 1)\)
又因为 \(sum(i, j) = a(i, j)\)
所以 \(d(i, j) = a(i, j) - a(i, j - 1) - a(i - 1, j) + a(i - 1, j - 1)\)
按照二维差分的修改:让 \((i, j)-(h, k)\) 的区域都 \(+k\),只需要在 \(d(i, j)\) 位置和 \(d(h + 1, k + 1) + k\) ,在 \(d(h + 1, j)\)\(d(i, k + 1) - k\) 即可,单点查询的做法等同于上面的区间查询
代码:

#include <bits/stdc++.h>
#define ll long long
#define rll register long long
using namespace std;
ll n, m, a, b, c, d, k, x, y, p;
ll s[4500][4500];
void add(ll, ll, ll);
ll sum(ll, ll);
int main() {
	scanf("%lld%lld", &n, &m);
	while ((scanf("%lld", &p)) != EOF) {
		if (p == 1) {
			scanf("%lld%lld%lld%lld%lld", &a, &b, &c, &d, &k);
			add(a, b, k);
			add(a, d + 1, -k);
			add(c + 1, b, -k);
			add(c + 1, d + 1, k);
		} 
		if (p == 2) {
			scanf("%lld%lld", &x, &y);
			printf("%lld\n", sum(x, y));
		}
	}
	return 0;
}
void add(ll xx, ll yy, ll zz) {
	for (rll i = xx; i <= n; i += (i & (-i))) {
		for (rll j = yy; j <= m; j += (j & (-j))) {
			s[i][j] += zz;
		}
	}
}
ll sum(ll xx, ll yy) {
	ll ans = 0; 
	for (rll i = xx; i; i -= (i & (-i))) {
		for (rll j = yy; j; j -= (j & (-j))) {
			ans += s[i][j];
		}
	}
	return ans;
}

区间修改,区间查询:
跟一维的公式解释差不多,区间查询,实际就是求 \(\sum_{i=1}^{x} \sum _{j=1}^{y} \sum_{h=1}^{i} \sum_{k=1}^j \ (d_{h, k})\)

\[\sum_{i=1}^{x} \sum _{j=1}^{y} \sum_{h=1}^{i} \sum_{k=1}^j \ (d_{h, k}) =\\ \sum_{i=1}^{x} \sum _{j=1}^{y}(d_{i, j} \times (x + 1 - i) \times (y + 1 - j)) \]

继续往下推

\[\sum_{i=1}^{x} \sum_{j=1}^{y}(d_{i, j} \times (x + 1 - i) \times (y + 1 - j)) =\\ (x + 1 - i) \times (y + 1) \times \sum_{i=1}^{x} \sum_{j=1}^{y}(d_{i, j}) - (j \times (x + 1 - i) \times \sum_{i=1}^{x} \sum_{j=1}^{y}(d_{i, j})) =\\ (x + 1) \times (y + 1) \times \sum_{i=1}^{x} \sum_{j=1}^{y}(d_{i, j}) - (y + 1) \times i \times \sum_{i=1}^{x} \sum_{j=1}^{y}(d_{i, j}) - (x + 1) \times j \times \sum_{i=1}^{x} \sum_{j=1}^{y}(d_{i, j}) + i \times j \times \sum_{i=1}^{x} \sum_{j=1}^{y}(d_{i, j}) \]

因此,我们只要维护 \(\sum_{i=1}^{x} \sum_{j=1}^{y}(d_{i, j})、\sum_{i=1}^{x} \sum_{j=1}^{y}(d_{i, j}) \times i、\sum_{i=1}^{x} \sum_{j=1}^{y}(d_{i, j}) \times j、\sum_{i=1}^{x} \sum_{j=1}^{y}(d_{i, j}) \times i \times j\) 即可
代码:

#include<bits/stdc++.h>
#define ll long long
#define rll register long long
using namespace std;
ll n,m;
ll s1[2400][2400],s2[2400][2400],s3[2400][2400],s4[2400][2400];
void add(ll,ll,ll);
ll sum(ll,ll);
int main()
{
	ll p,a,b,c,d,x;
	scanf("%lld%lld",&n,&m);
	while((scanf("%lld",&p))!=EOF)
	{
		if(p==1)
		{
			scanf("%lld%lld%lld%lld%lld",&a,&b,&c,&d,&x);
			add(a,b,x);
			add(a,d+1,-x);
			add(c+1,b,-x);
			add(c+1,d+1,x);
			}
		else
		{
			scanf("%lld%lld%lld%lld",&a,&b,&c,&d);
			printf("%lld\n",sum(c,d)-sum(c,b-1)-sum(a-1,d)+sum(a-1,b-1));
		}
	}
	return 0;
}
void add(ll x,ll y,ll z)
{
	for(rll i=x;i<=n;i+=(i&(-i)))
	{
		for(rll j=y;j<=m;j+=(j&(-j)))
		{
			s1[i][j]+=z;
			s2[i][j]+=z*x;
			s3[i][j]+=z*y;
			s4[i][j]+=z*x*y;
		}
	}
}
ll sum(ll x,ll y)
{
	ll ans=0;
	for(rll i=x;i;i-=(i&(-i)))
	{
		for(rll j=y;j;j-=(j&(-j)))
		{
			ans+=((x+1)*(y+1)*s1[i][j])-((y+1)*s2[i][j])-((x+1)*s3[i][j])+s4[i][j];
		}
	}
	return ans;
}
posted @ 2022-06-30 11:43  yi_fan0305  阅读(39)  评论(0编辑  收藏  举报