首页 > 编程语言 > 详细

树状数组专题(搬运

时间:2020-06-03 16:36:24      阅读:56      评论:0      收藏:0      [点我收藏+]

原来只是想把树状数组放到我的模板里的,但是越学越多,那就直接开个搬运专题吧

树状数组(搬运:

原文地址:https://blog.csdn.net/qq_35885746/article/details/89247993

 

树状数组实现单点与区间操作

由于最近经常被二维问题卡住,而且二维线段树日常写炸,于是来学习总结一下二维树状数组来缓解一下一遇到二维问题就拉闸的情况。

首先是最基本的单点修改+区间查询

先附上一维代码:

int lowbit(int x){
    return x&(-x);
}

void update(int i,int k){    
    while(i <= n){
        tree[i] += k;
        i += lowbit(i);
    }
}

int ask(int i){ 
    int res = 0;
    while(i > 0){
        res += tree[i];
        i -= lowbit(i);
    }
    return res;
}

这个好说,就是普通一维的一个小拓展,就直接上代码了:

int lowbit(int x)
{
    return x&-x;
}
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);
    }
}
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;
}

 

接下来是区间修改+单点查询

在思考这个问题以前,首先思考一维树状数组如何进行区间修改+单点查询

显然,通过一维差分可以解决这个问题

设原数组为技术分享图片, 设数组技术分享图片,则技术分享图片,可以通过求技术分享图片的前缀和查询

当给区间技术分享图片加上x的时候,技术分享图片与前一个元素 技术分享图片的差增加了x,技术分享图片与 技术分享图片的差减少了x。根据技术分享图片数组的定义,只需给技术分享图片加上 x, 给 技术分享图片减去x即可

代码如下:

void add(int x,int v)
{
    while(x<=n) 
        sum[x]+=v,x+=lowbit(x);
}

//这里的real_ask是用于区间修改L到R
void real_add(int l,int r,int v)
{
    add(l,x);
    add(r+1,-x);
}
int ask(int x)
{ 
    int res=0;
    while(x)
        res+=sum[x],x-=lowbit(x);
    return res;
}

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

二维前缀和:技术分享图片

我们可以令差分数组技术分享图片表示技术分享图片与 技术分享图片的差。

代码如下:

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);
    }
}
//与上面的real_add同理
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);
}

 

树状数组在树上实现单点与区间操作

 

树状数组专题(搬运

原文:https://www.cnblogs.com/Vikyanite/p/13037838.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!