BZOJ3132 上帝造题的七分钟题解(树状数组+差分)

题目:BZOJ3132.
题目大意:给定一个 n ∗ m n*m nm的矩阵初始全为 0 0 0,现在有 m m m个操作可能为:
1.给一个子矩阵加上一个数.
2.询问某个子矩阵的和.
1 ≤ n , m ≤ 2048 1\leq n,m\leq 2048 1n,m2048,询问数 q ≤ 2 ∗ 1 0 5 q\leq 2*10^5 q2105.

我们可以维护这个矩阵的差分数组 A i , j = a i , j − a i − 1 , j − a i , j − 1 + a i − 1 , j − 1 A_{i,j}=a_{i,j}-a_{i-1,j}-a_{i,j-1}+a_{i-1,j-1} Ai,j=ai,jai1,jai,j1+ai1,j1,那么矩阵加就变为四个单点加,但询问变复杂了.

我们考虑只需要求出 ( 1 , 1 ) (1,1) (1,1) ( x , y ) (x,y) (x,y)的子矩阵和,那么有:
∑ i = 1 x ∑ j = 1 y a i , j = ∑ i = 1 n ∑ j = 1 m ∑ k = 1 i ∑ t = 1 j A k , t = ∑ i = 1 n ∑ j = 1 m A k , t ( x − i + 1 ) ( y − j + 1 ) \sum_{i=1}^{x}\sum_{j=1}^{y}a_{i,j}=\sum_{i=1}^{n}\sum_{j=1}^{m}\sum_{k=1}^{i}\sum_{t=1}^{j}A_{k,t}=\sum_{i=1}^{n}\sum_{j=1}^{m}A_{k,t}(x-i+1)(y-j+1) i=1xj=1yai,j=i=1nj=1mk=1it=1jAk,t=i=1nj=1mAk,t(xi+1)(yj+1)

我们把括号内的系数展开:
( x − i + 1 ) ( y − j + 1 ) = x y − x ( j − 1 ) − y ( i − 1 ) + ( i − 1 ) ( j − 1 ) (x-i+1)(y-j+1)=xy-x(j-1)-y(i-1)+(i-1)(j-1) (xi+1)(yj+1)=xyx(j1)y(i1)+(i1)(j1)

我们只要用四个树状数组分别维护 A i , j , A i , j ( i − 1 ) , A i , j ( j − 1 ) , A i , j ( i − 1 ) ( j − 1 ) A_{i,j},A_{i,j}(i-1),A_{i,j}(j-1),A_{i,j}(i-1)(j-1) Ai,j,Ai,j(i1),Ai,j(j1),Ai,j(i1)(j1)即可.

时间复杂度 O ( q log ⁡ n log ⁡ m ) O(q\log n\log m) O(qlognlogm).

代码如下:

#include
using namespace std;
 
typedef long long LL;
 
const int N=2048;
 
char Rc(){
  char c=getchar();
  for (;c^'X'&&c^'L'&&c^'k'&&c!=EOF;c=getchar());
  return c;
}
 
int Ri(){
  int x=0,y=1;
  char c=getchar();
  for (;c<'0'||c>'9';c=getchar()) if (c=='-') y=-1;
  for (;c<='9'&&c>='0';c=getchar()) x=x*10+c-'0';
  return x*y;
}
 
int n,m;
 
void into(){
  Rc();n=Ri();m=Ri();
}
 
int c[4][N+9][N+9];
 
void Add(int id,int x,int y,int v){
  for (int i=x;i<=n;i+=i&-i)
    for (int j=y;j<=m;j+=j&-j) c[id][i][j]+=v;
}
 
void Add(int x,int y,int v){
  Add(0,x,y,v);
  Add(1,x,y,v*(x-1));
  Add(2,x,y,v*(y-1));
  Add(3,x,y,v*(x-1)*(y-1));
}
 
int Query(int id,int x,int y){
  int res=0;
  for (int i=x;i;i-=i&-i)
    for (int j=y;j;j-=j&-j) res+=c[id][i][j];
  return res;
}
 
int Query(int x,int y){
  int res=0;
  res+=x*y*Query(0,x,y);
  res-=y*Query(1,x,y);
  res-=x*Query(2,x,y);
  res+=Query(3,x,y);
  return res;
}
 
void getans(){
  char opt;
  for (;(opt=Rc())!=EOF;){
    int x0,y0,x1,y1;
    x0=Ri();y0=Ri();x1=Ri();y1=Ri();
    if (opt=='L'){
      int v=Ri();
      Add(x0,y0,v);
      Add(x0,y1+1,-v);
      Add(x1+1,y0,-v);
      Add(x1+1,y1+1,v);
    }else{
      int ans=0;
      ans+=Query(x1,y1);
      ans-=Query(x1,y0-1);
      ans-=Query(x0-1,y1);
      ans+=Query(x0-1,y0-1);
      printf("%d\n",ans);
    }
  }
}
 
int main(){
  into();
  getans();
  return 0;
}

你可能感兴趣的:(BZOJ3132 上帝造题的七分钟题解(树状数组+差分))