二维树状数组

二维的区间修改+单点查询可以用类似二维差分的方法来解决

二维前缀和:sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+a[i][j]

我们可以令差分数组d[i][j]表示a[i][j]与 a[i-1][j]+a[i][j-1]-a[i-1][j-1]的差。

代码如下:

void add(int x,int y,int v)
{
    while(x<=n)
    {
        int ty=y;
        while(ty<=n)
            tree[x][ty]+=v,ty+=lowbit(ty);
        x+=lowbit(x);
    }
}
void real_add(int x1,int y1,int x2,int y2,int v)
{
    add(x1,y1,v);
    add(x1,y2+1,-v);
    add(x2+1,y1,-v);
    add(x2+1,y2+1,v);
}
int ask(int x, int y)
{
    int res=0;
    while(x)
    {
        int ty=y;
        while(ty)
            res+=tree[x][ty],ty-=lowbit(ty);
        x-=lowbit(x);
    }
    return res;
}

最后思考如何区间修改+区间查询:

同样首先思考一维树状数组如何进行区间修改+区间查询

基于上文的差分思路,我们知道位置p的前缀和为 

在等式最右侧的式子中,被用了p次,被用了次……那么我们可以得出:

位置p的前缀和为 

于是我们可以维护两个数组的前缀和:
一个数组是 
另一个数组是 

位置p的前缀和即:数组中p的前缀和 - sum2数组中p的前缀和。

区间[l, r]的和即:位置r的前缀和 - 位置l的前缀和。

代码如下:


 

void add(int x,int v)
{
    int p=x;
    while(x<=n)
    {
        sum1[x]+=v;
        sum2[x]+=v*p;
        x+=lowbit(x);
    }
}
void real_add(int l,int r,int v)
{
    add(l,v),
    add(r+1,-v);
}
int ask(int x)
{
    int res=0;
    int p=x;
    while(x)
    {
        res+=(p+1)*sum1[x]-sum2[x];
        x-=lowbit(x);
    }
    return res;
}
int real_ask(int l,int r)
{
    return ask(r)-ask(l-1);
}

同理,二维的区间修改+区间查询也可以用类似的思路来解决

类比之前一维数组的区间修改区间查询,下面这个式子表示的是点(x, y)的二维前缀和:

首先,类比一维数组,统计一下每个出现过多少次。出现了次,出现了次……出现了 次。

那么这个式子就可以写成:

把这个式子展开,就得到:

那么我们要开四个树状数组,分别维护:

,,,

这样就可以解决上述问题了

代码如下:
 

void add(int x,int y,int v)
{
    for(int i=x;i<=n;i+=lowbit(i))
        for(int j=y;j<=m;j+=lowbit(j))
        {
            t1[i][j]+=v;
            t2[i][j]+=v*x;
            t3[i][j]+=v*y;
            t4[i][j]+=v*x*y;
        }
}
void real_add(int x1,int y1,int x2,int y2,int v)
{
    add(x1,y1,v);
    add(x1,y2+1,-v);
    add(x2+1,y1,-v);
    add(x2+1,y2+1,v);
}
int ask(int x, int y)
{
    int res=0;
    for(int i=x;i;i-=lowbit(i))
        for(int j=y;j;j-=lowbit(j))
            res+=(x+1)*(y+1)*t1[i][j]-(y+1)*t2[i][j]-(x+1)*t3[i][j]+t4[i][j];
    return res;
}
int real_ask(int x1,int y1,int x2,int y2)
{
    return ask(x2,y2)-ask(x2,y1-1)-ask(x1-1,y2)+ask(x1-1,y1-1);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值