二维线段树学习笔记(区间操作)

二维树状数组,其实就是在一维树状数组上加了一维罢了。
原理就不说了,主要说区间操作。
类比一维树状数组,一维的时候,对于数组a[ ],我们定义了一个drt[ ],drt[i]表示a[i]-a[i-1]。于是区间a[l..r]+=t就可以表示为 drt[l]+=t,drt[r+1]+=t; 了。
那怎么求a[l..r]的区间和呢?
只要求a[1..x]就ok了

\[\sum_{i=1}^x a[i] \]

\[=\sum_{i=1}^x (x-i+1)*d[i] \]

于是就维护d[i]和d[i]*i就OK了

那到了二维呢??
你们应该都知道二维前缀和吧

\[S[i][j]=\sum_{i=1}^x \sum_{j=1}^y a[i][j] \]

所以

\[a[i][j]=S[i][j]-S[i-1][j]-S[i][j-1]+S[i-1][j-1] \]

那我们令二维差分数组d[i][j]满足

\[a[i][j]=\sum_{i=1}^x \sum_{j=1}^y d[i][j] \]

所以

\[d[i][j]=a[i][j]-a[i-1][j]-a[i][j-1]+a[i-1][j-1] \]

所以我们要给a[i..j][k..l]+=t时
我们只要
d[i][k]+=t,d[j+1][k]-=t,d[i][l+1]-=t,d[j+1][l+1]+=t;
就好了。

那求区间和呢???
其实就是求a[1..x][1..y]求好了

\[\sum_{i=1}^x\sum_{j=1}^y a[i][j] \]

\[=\sum_{i=1}^x\sum_{j=1}^y d[i][j]*(x-i+1)*(y-j+1) \]

\[=\sum_{i=1}^x\sum_{j=1}^y d[i][j]*(xy+x+y+1 - (x+1)j - (y+1)*i + i*j) \]

于是维护

\[d[i][j],d[i][j]*i,d[i][j]*j,d[i][j]*i*j \]

就OK了

例题:https://www.luogu.org/problemnew/show/P4514
裸题。
code:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdlib>
using namespace std;
#define ll long long
#define mst(arr,x) memset(arr,x,sizeof(arr))
#define lowbit(x)   (x&(-x))
const int INF = 0x7fffffff;
const int N   = 2109;
int Read() {int ret=0,nag=1;char c=getchar();  while (c>'9'||c<'0') {if(c=='-') nag=-1;  c=getchar();}  while (c<='9'&&c>='0')    ret=(ret<<3)+(ret<<1)+c-'0',c=getchar();  return ret*nag;}
void Print(int x) {if(x<0) putchar('-'),x=-x;  if(x>9) Print(x/10);  putchar(x%10+'0');}
int n,m,d0[N][N],di[N][N],dj[N][N],dij[N][N];
char opt[3];
void Add(int x,int y,int t)
{
    for(int i=x;i<=n;i+=lowbit(i))
        for(int j=y;j<=m;j+=lowbit(j))
            d0[i][j]+=t,di[i][j]+=t*x,dj[i][j]+=t*y,dij[i][j]+=t*x*y;
}
int Ask(int x,int y)
{
    int ret=0;
    for(int i=x;i;i-=lowbit(i))
        for(int j=y;j;j-=lowbit(j))
            ret+=d0[i][j]*(x*y+x+y+1)-di[i][j]*(y+1)-dj[i][j]*(x+1)+dij[i][j];
    return ret;
}
inline int Sum(int a,int b,int c,int d)    {return Ask(c,d)-Ask(a-1,d)-Ask(c,b-1)+Ask(a-1,b-1);}
inline void Modify(int a,int b,int c,int d,int t){Add(a,b,t),Add(a,d+1,-t),Add(c+1,b,-t),Add(c+1,d+1,t);}
int main ()
{
    #ifndef ONLINE_JUDGE
    freopen("INPUT.in","r",stdin);
    freopen("OUTPUT.out","w",stdout);
    #endif
    scanf("%*c%d%d",&n,&m);
    while(scanf("%s",opt)!=EOF)
    {
        if(opt[0]=='L')
        {
            int a=Read(),b=Read(),c=Read(),d=Read(),t=Read();
            Modify(a,b,c,d,t);
        }
        else
        {
            int a=Read(),b=Read(),c=Read(),d=Read();
            Print(Sum(a,b,c,d)),puts("");
        }
        
    }
    return 0;
}
posted @ 2019-03-10 20:29  bjxdw  阅读(252)  评论(0编辑  收藏  举报