二维树状数组

文章出处

题意:

给定n×m的矩阵,k个操作,每个操作给定矩阵左上角和右下角顶点,有两种操作,一是将该矩阵全部元素加上xx,二是询问该矩阵全部元素的和。
数据范围:0n,m2048,k200000,0x500                0 ≤n,m≤2048,k≤200000,0≤x≤500,保证计算过程中所有数不超过带符号整型。

分析:

区间求和问题我们可以转化为前缀和问题,即对于[a,b][c,d][a,b]和[c,d]之间的矩阵答案为sum(c,d)sum(c,b1)sum(a1,d)+sum(a,b)sum(c,d)−sum(c,b−1)−sum(a−1,d)+sum(a,b),这样就可以变为单点查询。
对于区间修改问题,我们利用差分的思想,将区间修改转化为单点修改。
令每个位置的元素为newai,j=ai,jai1,jai,j1+ai1,j1newai,j=ai,j−ai−1,j−ai,j−1+ai−1,j−1,那么ax,y=xi=1yj=1newai,jax,y=∑i=1x∑j=1ynewai,j
sum(a,b)=ax=1by=1ax,y=ax=1by=1xi=1yj=1newai,jsum(a,b)=∑x=1a∑y=1bax,y=∑x=1a∑y=1b∑i=1x∑j=1ynewai,j
考虑[1,1][1,1]内每个元素对于sum(a,b)sum(a,b)的贡献,
sum(a,b)=ai=1bj=1(a+1i)×(b+1j)×newax,ysum(a,b)=∑i=1a∑j=1b(a+1−i)×(b+1−j)×newax,y
=(a+1)×(b+1)ai=1bj=1newai,j(a+1)ai=1bj=1newai,j×j(b+1)ai=1bj=1newai,j×i=(a+1)×(b+1)∑i=1a∑j=1bnewai,j−(a+1)∑i=1a∑j=1bnewai,j×j−(b+1)∑i=1a∑j=1bnewai,j×i
+ai=1bj=1i×j×newai,j+∑i=1a∑j=1bi×j×newai,j

这样对于每个点分别记录这四个值,单点更新单点查询套个二维树状数组即可。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<algorithm>
using namespace std;
const int maxn = 2500 + 5;
int bit[maxn][maxn][5];
void add(int x, int y, int val, int id) 
{
	for (int i = x; i < maxn; i += i & (-i)) {
		for (int j = y; j < maxn; j += j & (-j)){
			bit[i][j][id] += val;
		}
	}
}
int sum(int x, int y, int id)
{
	int res = 0;
	for(int i = x; i; i -= i & (-i)){
		for(int j = y; j; j -= j & (-j)){
			res += bit[i][j][id];
		}
	}
	return res;
}
void Add(int a, int b, int dt)
{
	add(a, b, dt, 1); add(a, b, dt * b, 2); add(a, b, dt * a, 3); add(a, b, dt * a * b, 4);
}
int Query(int c, int d)
{
	int ans = (c + 1) * (d + 1) * sum(c, d, 1) - (d + 1) * sum(c, d, 3) - (c + 1) * sum(c, d, 2) + sum(c, d, 4); 
	return ans;
}
char s[10];
int main (void)
{
	int n, m;scanf("%s%d%d", s, &n, &m);
	int a, b, c, d, dt;
	memset(bit, 0 ,sizeof(bit));
	while(~scanf("%s", s)){
		if(s[0] == 'L'){
			scanf("%d%d%d%d%d", &a, &b, &c, &d, &dt);	
			Add(a, b, dt);
			Add(a, d + 1, -dt); 
			Add(c + 1, b, -dt);
			Add(c + 1, d + 1, dt);
		}else{
			scanf("%d%d%d%d", &a, &b, &c, &d);
			if(a > c || b > d) swap(a, b), swap(c, d);
			printf("%d\n", Query(c, d) - Query(a - 1, d) - Query(c, b - 1) + Query(a - 1, b - 1));
		}
	}
	return 0;
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值