题目请戳这里
题目大意:给一个矩阵,然后q个操作。有3种操作类型:
op = 0:按行优先原则从(x1,y1)到(x2,y2)所有元素都加一个k。
op = 1:按列优先原则从(x1,y1)到(x2,y2)所有元素都加上k。
op = 2:查询(x,y)处元素值。
题目分析:矩阵不大,但是查询很多。暴力会超时。于是高兴的写了一个二维线段树,结果华丽丽的TLE了。然后又不断调整姿势,还是TLE。。。
唉,二维线段树,感觉再也不会爱了。。。
这道题仔细看只有2种操作:区间修改和单点查询。所以这题可以直接用二维树状数组做。因为是修改区间查询点,所以要向下修改向上查询。
更新的时候,以行优先为例,得出来的更新区域不是一个完整的矩形。所以可以考虑将更新区域分解。有2种分法:做加法和做减法。2种方法都差不多,都是将更新区域分成3部分。我是用的减法,感觉方便些。
详情请见代码:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N = 101; int bit[N][N]; int n,m,c1,c2,r1,r2,op,k,s; void init() { for(int i = 1;i <= n;i ++) for(int j = 1;j <= m;j ++) bit[i][j] = 0; } void add(int x,int y,int val) { if(x <= 0 || y <= 0) return; while(x) { int i = y; while(i) { bit[x][i] += val; i -= (i&-i); } x -= (x&-x); } } int sum(int x,int y) { int ret = 0; while(x <= n) { int i = y; while(i <= m) { ret += bit[x][i]; i += (i&-i); } x += (x&-x); } return ret; } void addretc(int x1,int y1,int x2,int y2,int val) { if(x1 > x2 || y1 > y2) return ; add(x1 - 1,y1 - 1,val); add(x2,y2,val); add(x1 - 1,y2,-val); add(x2,y1 - 1,-val); } int main() { int cas = 0; while(scanf("%d%d",&n,&m) != EOF) { init(); int tmp; // if(cas) // puts(""); for(int i = 1;i <= n;i ++) for(int j = 1;j <= m;j ++) scanf("%d",&tmp),addretc(i,j,i,j,tmp); scanf("%d",&s); printf("Case %d\n",++cas); while(s --) { scanf("%d",&op); if(op == 2) { scanf("%d%d",&r1,&c1); printf("%d\n",sum(r1,c1)); } else { scanf("%d%d%d%d%d",&r1,&c1,&r2,&c2,&k); if(!op) { if(r1 > r2) swap(r1,r2),swap(c1,c2); else if(r1 == r2 && c1 > c2) swap(c1,c2); addretc(r1,1,r2,m,k); addretc(r1,1,r1,c1 - 1,-k); addretc(r2,c2 + 1,r2,m,-k); } else { if(c1 > c2) swap(c1,c2),swap(r1,r2); else if(c1 == c2 && r1 > r2) swap(r1,r2); addretc(1,c1,n,c2,k); addretc(1,c1,r1 - 1,c1,-k); addretc(r2 + 1,c2,n,c2,-k); } } } } return 0; }