看到题目一眼 $cdq$ 分治,然后发现 $n,m$ 很小,感到一丝不对劲
然后去看看题解发现正解是二维树状数组
二维树状数组和一维的好像也差不多?
struct BIT { int t[N][N]; inline void add(int x,int y,int v) { for(;x<=n;x+=x&-x) for(int j=y;j<=m;j+=j&-j) t[x][j]+=v; } inline int ask(int x,int y) { int res=0; for(;x;x-=x&-x) for(int j=y;j;j-=j&-j) res+=t[x][j]; return res; } };
题目要求的是矩形修改矩形询问,考虑差分并用差分数组求原数组二维前缀和再容斥一下,设差分矩阵为 $A$
那么对于一个二维前缀和的询问 $(x,y)$ ,差分矩阵某个位置 $(a,b),a leq x,b leq y$ 被统计当且仅当 $x' leq x,y' leq y$ 并且 $a leq x' ,b leq y'$
显然这样的 $(x',y')$ 有 $(x-a+1)*(y-b+1)$ 对,那么这个位置 $(a,b)$ 就会被统计到 $(x-a+1)*(y-b+1)$ 次,所以贡献为 $A[a][b]*(x-a+1)*(y-b+1)$
把后面拆开 $(xy-xb+x-ay+ab-a+y-b+1)=(xy+x+y+1)-(x+1)b-(y+1)a+ab$
所以对于二维前缀和的询问 $(x,y)$ 我们只要开四个树状数组分别维护 $A[a][b],A[a][b]*b,A[a][b]*a,A[a][b]*ab$ 即可
最后容斥一下就是答案了
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> using namespace std; typedef long long ll; inline int read() { int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } const int N=2049,M=2e5+7; int n,m; struct BIT { int t[N][N]; inline void add(int x,int y,int v) { for(;x<=n;x+=x&-x) for(int j=y;j<=m;j+=j&-j) t[x][j]+=v; } inline int ask(int x,int y) { int res=0; for(;x;x-=x&-x) for(int j=y;j;j-=j&-j) res+=t[x][j]; return res; } }T,Ti,Tj,Tij; inline void ADD(int x,int y,int v) { T.add(x,y,v); Ti.add(x,y,v*x); Tj.add(x,y,v*y); Tij.add(x,y,v*x*y); } inline int ASK(int x,int y) { return T.ask(x,y)*(x*y+x+y+1)+Tij.ask(x,y) -Ti.ask(x,y)*(y+1)-Tj.ask(x,y)*(x+1); } int main() { char s[7]; scanf("%s",s); n=read(),m=read(); int a,b,c,d,v; while(scanf("%s",s)!=EOF) { a=read(),b=read(),c=read(),d=read(); if(s[0]=='L') { v=read(); ADD(a,b,v); ADD(c+1,d+1,v); ADD(a,d+1,-v); ADD(c+1,b,-v); } else printf("%d ",ASK(a-1,b-1)+ASK(c,d)-ASK(a-1,d)-ASK(c,b-1)); } return 0; }