树状数组学习

参考博客
https://www.cnblogs.com/xenny/p/9739600.html
https://www.cnblogs.com/RabbitHu/p/BIT.html
https://blog.csdn.net/bestsort/article/details/80796531

理论方面和具体的应用方面可以参考上面博客
这里直接贴模板

单点跟新+区间查询(普通建树)
int n;
int a[50005],c[50005]; //对应原数组和树状数组

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

void updata(int i,int k){    //在i位置加上k
    while(i <= n){
        c[i] += k;
        i += lowbit(i);
    }
}

int getsum(int i){        //求A[1 - i]的和
    int res = 0;
    while(i > 0){
        res += c[i];
        i -= lowbit(i);
    }
    return res;
}
单点查询+区间更新(差分建树)
int n,m;
int a[50005] = {0},c[50005]; //对应原数组和树状数组

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

void updata(int i,int k){    //在i位置加上k
    while(i <= n){
        c[i] += k;
        i += lowbit(i);
    }
}

int getsum(int i){        //求D[1 - i]的和,即A[i]值
    int res = 0;
    while(i > 0){
        res += c[i];
        i -= lowbit(i);
    }
    return res;
}

int main(){
    cin>>n;
    for(int i = 1; i <= n; i++){
        cin>>a[i];
        updata(i,a[i] - a[i-1]);   //输入初值的时候,也相当于更新了值
    }
    
    //[x,y]区间内加上k
    updata(x,k);    //A[x] - A[x-1]增加k
    updata(y+1,-k);        //A[y+1] - A[y]减少k
    
    //查询i位置的值
    int sum = getsum(i);

    return 0;
}
区间更新+区间查询

维护两个数状数组,sum1[i] = D[i],sum2[i] = D[i]*(i-1);

int n,m;
int a[50005] = {0};
int sum1[50005];    //(D[1] + D[2] + ... + D[n])
int sum2[50005];    //(1*D[1] + 2*D[2] + ... + n*D[n])

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

void updata(int i,int k){
    int x = i;    //因为x不变,所以得先保存i值
    while(i <= n){
        sum1[i] += k;
        sum2[i] += k * (x-1);
        i += lowbit(i);
    }
}

int getsum(int i){        //求前缀和
    int res = 0, x = i;
    while(i > 0){
        res += x * sum1[i] - sum2[i];
        i -= lowbit(i);
    }
    return res;
}

int main(){
    cin>>n;
    for(int i = 1; i <= n; i++){
        cin>>a[i];
        updata(i,a[i] - a[i-1]);   //输入初值的时候,也相当于更新了值
    }

    //[x,y]区间内加上k
    updata(x,k);    //A[x] - A[x-1]增加k
    updata(y+1,-k);        //A[y+1] - A[y]减少k

    //求[x,y]区间和
    int sum = getsum(y) - getsum(x-1);

    return 0;
}

区间修改、单点查询模板题目:https://www.luogu.org/problemnew/show/P3368
区间修改、区间查询模板题目:https://vjudge.net/problem/POJ-3468


二维树状数组

参考博客:https://blog.csdn.net/zzti_xiaowei/article/details/81053094
感觉讲的非常不错,例子应该花了不少时间,很容易懂

单点修改+区间查询
const int maxn = 1003;
int maps[maxn][maxn];
 
int lowbit(int x)
{
    return x & (-x);
}
 
void add(int x , int y , int d)
{
    for(int i = x ; i < maxn ; i += lowbit(i))
    {
        for(int j = y ; j < maxn ; j += lowbit(j))
        {
            maps[i][j] += d;
        }
    }
}
 
void init()//可要可不要
{
    memset(maps , 0 , sizeof(maps));
    for(int i = 1 ; i < maxn; i ++)
    {
        for(int j = 1 ; j < maxn ; j ++)
        {
            add(i , j , 1);
        }
    }
}
 
 
int sum(int x , int y)//求的是map[1][1]到map[x][y]的矩阵和
{
    int res = 0;
    for(int i = x ; i > 0 ; i -= lowbit(i))
    {
        for(int j = y ; j > 0 ; j -= lowbit(j))
        {
            res += maps[i][j];
        }
    }
    return res;
}

int query(int x1,int y1,int x2,int y2){   //区间查询,[x1][y1]到[x2][y2]的子矩阵
    return sum(x2,y2)+sum(x2,y2)-sum(x2,y1-1)-sum(x1-1,y2);  //容斥,注意是否可能超longlong
}

区间修改+单点查询
//区间修改,单点查询,和一维树状数组差分思想一样,差分思想。
//二维前缀和:
//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 regionUpdate(int x1,int y1,int x2,int y2,int val){
    add(x1,y1,val);
    add(x2+1,y1,-val);
    add(x1,y2+1,-val);
    add(x2+1,y2+1,val);
}
int pointQuery(int x,int y){
	return sum(x,y);
}
区间更新+区间查询

参考博客:https://www.cnblogs.com/RabbitHu/p/BIT.html

ll n, m, Q;
ll t1[N][N], t2[N][N], t3[N][N], t4[N][N];
void add(ll x, ll y, ll z){
    for(int X = x; X <= n; X += X & -X)
        for(int Y = y; Y <= m; Y += Y & -Y){
            t1[X][Y] += z;
            t2[X][Y] += z * x;
            t3[X][Y] += z * y;
            t4[X][Y] += z * x * y;
        }
}
void range_add(ll xa, ll ya, ll xb, ll yb, ll z){ //(xa, ya) 到 (xb, yb) 的矩形
    add(xa, ya, z);
    add(xa, yb + 1, -z);
    add(xb + 1, ya, -z);
    add(xb + 1, yb + 1, z);
}
ll ask(ll x, ll y){
    ll res = 0;
    for(int i = x; i; i -= i & -i)
        for(int j = y; j; j -= j & -j)
            res += (x + 1) * (y + 1) * t1[i][j]
                - (y + 1) * t2[i][j]
                - (x + 1) * t3[i][j]
                + t4[i][j];
    return res;
}
ll range_ask(ll xa, ll ya, ll xb, ll yb){
    return ask(xb, yb) - ask(xb, ya - 1) - ask(xa - 1, yb) + ask(xa - 1, ya - 1);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值