树状数组(小总结)

大兔子大兔子 提交于 2019-11-30 19:55:07

树状数组


经典应用类型

  1. 求区间[l ,r]的和(乘积等);修改单点的值
  2. 求单点的值,修改区间[l ,r]的值

(类似线段树)

特性

类似线段树,但是常数更小,实现起来更简单,当然,功能不如线段树

数据结构

数组a[1…n]为原始数据
数组d[1…n]记录着区间和(乘积等)
数组d对应数组a的关系如下:

  1. 设节点编号为x,那么这个节点管辖的区间为2^k(其中k为x二进制末尾0的个数)个元素。因为这个区间最后一个元素必然为Ax,
  2. 所以很明显:d[n] = A(n – 2^k + 1) + ... + A[n]

实现方法


基本函数

  1. 计算2^k的函数
int lowbit(int x) {
	return x&(-x);
}
  1. 插入操作(将a[n]加v,维护d数组)
void modify(int x, int v) {/*Add v y a[x]*/
    while(x<=n) {
        d[x] += v;
        x += lowbit(x);
    }
}
  1. 查询操作(返回区间[1…x]的和前缀和
int query(int x) {/*Query from a[x] + ... + a[y]*/
    int sum = 0;
    while(x) {
        sum += d[x];
        x -= lowbit(x);
    }
    return sum;
}

emm…这么少
是的,就这么少

具体操作


点修改,区间查询

  1. 建树
    a[i] = temp
for(int i=1; i<=n; i++) {
        cin>>temp;
        modify(i, temp);
    }
  1. 修改
    把a[x] 加上y
modify(x, y);
  1. 查询
    返回区间a[x,y]的和
query(y) - query(x-1);

区间修改, 点查询

  1. 建树
    a[i] = temp
int temp, temp2 = 0;
for(int i=1; i<=n; i++){
	cin>>temp;
	modify(i, temp - temp2);//注意!!!
	temp2 = temp;
}
  1. 修改
    区间a[l, r]加上x
modify(l, x);
modify(r+1, -x);
  1. 查询
    返回a[x]
query(x);

几个🌰例子


点修改,区间查询

#include<iostream>
using namespace std;
const int maxn = 1000000;
int d[maxn] , n;

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

int query(int x) {/*Query from a[x] + ... + a[y]*/
    register int sum = 0;
    while(x) {
        sum += d[x];
        x -= lowbit(x);
    }
    return sum;
}

void modify(int x, int v) {/*Add v y a[x]*/
    while(x<=n) {
        d[x] += v;
        x += lowbit(x);
    }
}

int main(int argc, char const *argv[])
{
    #ifndef ONLINE_JUDGE
    //freopen("test.in", "r", stdin);
    #endif
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    //debug stuff
    int m, temp;
    cin>>n>>m;
    for(int i=1; i<=n; i++) {
        cin>>temp;
        modify(i, temp);//cout<<temp<<endl;
    }
    int option, x, y;
    for(int i=0; i<m; i++) {
        cin>>option>>x>>y;
        if(option==1)
            modify(x, y);
        else if(option==2) 
            cout<<query(y) - query(x-1)<<endl;
    }
    return 0;
}


区间修改, 点查询

#include<iostream>
using namespace std;
/*这是模板, 于ubuntu bash生成*/

const int maxn = 500000 + 10000;

int d[maxn], n, m;

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

int query(int x) {/*Query from a[x] + ... + a[y]*/
    register int sum = 0;
    while(x) {
        sum += d[x];
        x -= lowbit(x);
    }
    return sum;
}

void modify(int x, int v) {/*Add v y a[x]*/
    while(x<=n) {
        d[x] += v;
        x += lowbit(x);
    }
}

int main(int argc, char const *argv[])
{
    #ifndef ONLINE_JUDGE
    //freopen("test.in", "r", stdin);
    #endif
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    cin>>n>>m;
    int temp, temp2 = 0, option, l, r, x;
    for(int i=1; i<=n; i++)
        cin>>temp, modify(i, temp - temp2), temp2 = temp;
    for(int i=1; i<=m; i++) {
        cin>>option;
        if(option==1) {
            cin>>l>>r>>x;
            modify(l, x);
            modify(r+1, -x);
        }
        else {
            cin>>x;
            cout<<query(x)<<endl;
        }
    }
    return 0;
}


华丽的分割线END2019-10-07 14:47:22 星期一

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!