BZOJ-3132&&tyvj: 上帝造题的七分钟 (二维树状数组题解)

思路:

令数组f[i][j]代表从矩阵[i,j]-[n,m]的增量,那么我们每次修改(对矩阵[a,b]-[c,d]增加x)时就只需要执行以下4个操作:

f[a][b]+=w,f[a][d+1]-=w,f[c+1][d]-=w,f[c+1][d+1]+=w。

那么,很明显,对于一个位置[x,y],全部增量就是sigma(i=1->x,j=1->y,f[i][j]),(对于一个位置[x,y],全部增量为s[x][y])

那么,对于一个矩阵[1,1]-[x,y](可以理解为二维的前缀和)的总增量为s[1][1]+s[1][2]+…+s[x][y],

用归纳法可以发现,矩阵中某一位置i,j的增量f[i,j]在上式中出现次数为(x+1-i)(y+1-j)=(x+1)(y+1)-i(y+1)-j(x+1)+ij,

所以s[1][1]+s[1][2]+…+s[x][y]

=sigma(x+1-i)(y+1-j)f[i][j]

=sigma((x+1)(y+1)f[i][j]-i(y+1)f[i][j]-j(x+1)f[i][j]+ijf[i][j])

=(x+1)(y+1)sigmaf[i][j]-(y+1)sigma(if[i][j])-(x+1)sigma(jf[i][j])+sigma(ijf[i][j])

所以,我们可以用4个二维树状数组来维护f[i][j],if[i][j],jf[i][j],ijf[i][j]的前缀和即可。

代码(26行)(请原谅我无耻的压行~):

#include<cstdio>
#include<cstring>
#define MAXN 2050
#define clear(x) memset(x,0,sizeof(x))
#define lowbit(x)(((~(x))+1)&x)
#define For(i,x)for(int i=x;i<=n;i+=lowbit(i))
#define FOR(i,x)for(int i=x;i;i-=lowbit(i))
int T[MAXN][MAXN],A[MAXN][MAXN],B[MAXN][MAXN],C[MAXN][MAXN],n,m;
void Add(int x,int y,int z)
{For(i,x)For(j,y)T[i][j]+=z,A[i][j]+=z*x,B[i][j]+=z*y,C[i][j]+=z*x*y;}
int S(int x,int y){
    int Q=0,W=0,E=0,R=0;
    FOR(i,x)FOR(j,y)Q+=T[i][j],W+=A[i][j],E+=B[i][j],R+=C[i][j];
    return Q*(x+1)*(y+1)-W*(y+1)-E*(x+1)+R;
}
int main(){
    int a,b,c,d,X;
    char s[1];
    scanf("%s%d%d",&s,&n,&m);
    clear(T),clear(A),clear(B),clear(C);
    while(scanf("%s",&s)!=EOF){
        if(s[0]=='k') scanf("%d%d%d%d",&a,&b,&c,&d),printf("%d\n",S(c,d)-S(a-1,d)-S(c,b-1)+S(a-1,b-1));
        else scanf("%d%d%d%d%d",&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);
    }
    return 0;
}
    原文作者:AmadeusChan
    原文地址: https://www.jianshu.com/p/2f9b6e600012
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞