我是靠谱客的博主 传统玉米,最近开发中收集的这篇文章主要介绍树状数组详解 树 状 数 组 详 解 树状数组详解 树状数组详解 ,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

树 状 数 组 详 解 树状数组详解

先来看几个问题吧。

1. 什么是树状数组?

顾名思义,就是用数组来模拟树形结构呗。那么衍生出一个问题,为什么不直接

建树?答案是没必要,因为树状数组能处理的问题就没必要建树。和 T r i e Trie Trie 树的构造

方式有类似之处。

2. 树状数组可以解决什么问题

可以解决大部分基于区间上的更新以及求和问题。

3. 树状数组和线段树的区别在哪里

树状数组可以解决的问题都可以用线段树解决,这两者的区别在哪里呢?树状数

组的系数要少很多,就比如字符串模拟大数可以解决大数问题,也可以解决 1 + 1+ 1+

1 1 1 的问题,但没人会在 1 + 1 1+1 1+1 的问题上用大数模拟。

4. 树状数组的优点和缺点

修改和查询的复杂度都是 O ( l o g N ) O(logN) O(logN) ,而且相比线段树系数要少很多,比传统数

组要快,而且容易写。

  • 缺点是遇到复杂的区间问题还是不能解决,功能还是有限。

L O W B I T LOWBIT LOWBIT

敲 重 点 ! ! 敲重点!!

先给出一张图

不难得出 :

  • C [ 1 ] = A [ 1 ] C[1] = A[1] C[1]=A[1]
  • C [ 2 ] = A [ 1 ] + A [ 2 ] C[2] = A[1] + A[2] C[2]=A[1]+A[2]
  • C [ 3 ] = A [ 3 ] C[3] = A[3] C[3]=A[3]
  • C [ 4 ] = A [ 1 ] + A [ 2 ] + A [ 3 ] + A [ 4 ] C[4] = A[1] + A[2] + A[3] + A[4] C[4]=A[1]+A[2]+A[3]+A[4]
  • C [ 5 ] = A [ 5 ] C[5] = A[5] C[5]=A[5]
  • C [ 6 ] = A [ 5 ] + A [ 6 ] C[6] = A[5] + A[6] C[6]=A[5]+A[6]
  • C [ 7 ] = A [ 7 ] C[7] = A[7] C[7]=A[7]
  • C [ 8 ] = A [ 1 ] + A [ 2 ] + A [ 3 ] + A [ 4 ] + A [ 5 ] + A [ 6 ] + A [ 7 ] + A [ 8 ] C[8] = A[1] + A[2] + A[3] + A[4] + A[5] + A[6] + A[7] + A[8] C[8]=A[1]+A[2]+A[3]+A[4]+A[5]+A[6]+A[7]+A[8]

可以发现,这颗树是有规律的

C [ i ] = A [ i − 2 k + 1 ] + A [ i − 2 k + 2 ] + . . . + A [ i ] C[i] = A[i - 2k+1] + A[i - 2k+2] + ... + A[i] C[i]=A[i2k+1]+A[i2k+2]+...+A[i] ( k k k i i i 的二进制中从最低位到高位连续零的长度 )

那么如何求出二进制中从最低位到高位连续零的长度呢 ?

2 k = i   &   ( − i ) 2 ^ k = i & ( -i ) 2k=i & (i)

(我也不懂)

简单来说,树状数组其实其实就是把一个数存在它的二进制中为 1 1 1 的位中。

每次就找一个数的二进制中最后一个 1 1 1 的位置。

例:

7 7 7 的二进制为 00000111 00000111 00000111

7 7 7 的反码为 11111001 11111001 11111001

00000111 00000111 00000111 11110111 11110111 11110111 进行按位与得, 00000001 00000001 00000001

l o w b i t lowbit lowbit的实现

inline int lowbit(int a)
{
    return a & (-a);
}

一 维 树 状 数 组 一维树状数组

- 单点修改,区间查询

  • 模板
  • 单点修改
inline void update(int x, int y)
{
    while (x <= n)
    {
        c[x] += y;
        x += lowbit(x);
    }
}
  • 区间查询
    要想求出 x → y xto y xy 相当于 1 → y 1to y 1y 减去 1 → ( x − 1 ) 1to ( x-1 ) 1(x1)
inline int query(int x)
{
    int ans = 0;
    while (x)
    {
        ans += c[x];
        x -= lowbit(x);
    }
    return ans;
}

故 ,

修改 : u p d a t e ( x , v a l ) update(x,val) update(x,val)

查询 : q u e r y ( y ) − q u e r y ( x − 1 ) query(y)−query(x-1) query(y)query(x1)

A C   c o d e AC code AC code

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int maxn = 1e6 + 5;
int n, q, a[maxn], c[maxn];
inline int lowbit(int a)
{
    return a & (-a);
}
inline void update(int x, int y)
{
    while (x <= n)
    {
        c[x] += y;
        x += lowbit(x);
    }
}
inline int query(int x)
{
    int ans = 0;
    while (x)
    {
        ans += c[x];
        x -= lowbit(x);
    }
    return ans;
}
signed main()
{
    cin >> n >> q;
    for (int i = 1; i <= n; i++)
        cin >> a[i], update(i, a[i]);
    for (int i = 1; i <= q; i++)
    {
        int x, y, z;
        cin >> x >> y >> z;
        if (x == 1)
        {
            update(y, z);
        }
        else
        {
            cout << query(z) - query(y - 1) << endl;
        }
    }
    return 0;
}

- 区间修改,单点查询

  • 模板

  • 处理

    先弄个差分树状数组

    update(i, a[i] - a[i - 1]);
    
  • 区间修改

    利用前缀和的特性来 O ( 1 ) O(1) O(1) 更新区间的值。

    当区间 [ l , r ] [l, r] [l,r] 加上 k k k 时, a [ l ] a[l] a[l] a [ l − 1 ] a[l-1] a[l1] 的差增加了 k k k a [ r + 1 ] a[r+1] a[r+1] a [ r ] a[r] a[r] 的差减少了 k k k

    inline void update(int x, int y)
    {
    	while (x <= n)
    	{
        	c[x] += y;
        	x += lowbit(x);
    	}
    }
    inline void add(int l, int r, int x)
    {
    	update(l, x);
    	update(r + 1, -x);
    }
    
  • 单点查询

    inline int query(int x)
    {
    	int ans = 0;
    	while (x)
    	{
        	ans += c[x];
        	x -= lowbit(x);
    	}
    	return ans;
    }
    

故 ,

修改 : u p d a t e ( l , x )      u p d a t e ( r + 1 , − x ) update(l, x) update(r + 1, -x) update(l,x)    update(r+1,x)

查询 : q u e r y ( x ) query(x) query(x)

A C   c o d e AC code AC code

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int maxn = 1e6 + 5;
int n, q, a[maxn], maxx, c[maxn];
inline int lowbit(int a)
{
    return a & (-a);
}
inline void update(int x, int y)
{
    while (x <= n)
    {
        c[x] += y;
        x += lowbit(x);
    }
}
inline void add(int l, int r, int x)
{
    update(l, x);
    update(r + 1, -x);
}
inline int query(int x)
{
    int ans = 0;
    while (x)
    {
        ans += c[x];
        x -= lowbit(x);
    }
    return ans;
}
signed main()
{
    cin >> n >> q;
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
        update(i, a[i] - a[i - 1]);
    }
    for (int i = 1; i <= q; i++)
    {
        int abc, x, y, z;
        cin >> abc;
        if (abc == 1)
        {
            cin >> x >> y >> z;
            add(x, y, z);
        }
        else
        {
            cin >> x;
            cout << query(x) << endl;
        }
    }
    return 0;
}

- 区间修改,区间查询

树状数组 :区间修改,区间查询


华丽的结束线

不 , 没完

二 维 树 状 数 组 二维树状数组

1.单点修改,区间查询

查询时要用到容斥原理

A C   c o d e AC code AC code

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int maxn = 5001;
int n, m;
int tree[maxn][maxn];
int lowbit(int x)
{
    return x & -x;
}
/
void update(int x, int y, int z)
{
    for (int i = x; i <= n; i += lowbit(i))
    {
        for (int j = y; j <= m; j += lowbit(j))
        {
            tree[i][j] += z;
        }
    }
}
int query(int x, int y)
{

    int res = 0;
    for (int i = x; i; i -= lowbit(i))
    {
        for (int j = y; j; j -= lowbit(j))
        {
            res += tree[i][j];
        }
    }
    return res;
}
/
signed main()
{
    cin >> n >> m;
    int op;
    while (scanf("%lld", &op) != EOF)
    {
        int a, b, c, d;
        cin >> a >> b >> c;
        if (op == 1)
        {
            update(a, b, c);
        }
        else
        {
            cin >> d;
            cout << query(c, d) - query(a - 1, d) - query(c, b - 1) + query(a - 1, b - 1) << endl;
        }
    }
}
  1. 区间修改,单点查询

这里也需要用到差分 (二维啊!!!)

差分数组 d [ i ] [ j ] = a [ i ] [ j ] − a [ i − 1 ] [ j ] − a [ i ] [ j − 1 ] + a [ i − 1 ] [ j − 1 ] d[i][j]=a[i][j] - a[i-1][j] - a[i][j-1]+a[i-1][j-1] d[i][j]=a[i][j]a[i1][j]a[i][j1]+a[i1][j1]

部 分 代 码 部分代码

void update(int x, int y, int k)
{
    for (int i = x; i <= n; i += lowbit(i))
    {
        for (int j = y; j <= m; j += lowbit(j))
        {
            t[i][j] += k;
        }
    }
}

void range_update(int a, int b, int c, int d, int k)
{
    update(a, b, k);
    update(a, d + 1, -k);
    update(c + 1, b, -k);
    update(c + 1, d + 1, k);
}
  1. 区间修改,区间查询

下面这个式子表示的是点(x, y)的二维前缀和:

∑ i = 1 x ∑ j = 1 y ∑ k = 1 i ∑ h = 1 j d [ h ] [ k ] sum_{i=1}^{x}sum_{j=1}^{y}sum_{k=1}^{i}sum_{h=1}^{j}d[h][k] i=1xj=1yk=1ih=1jd[h][k]

时间复杂度 O ( l o g 2   n ) O(log_2 n) O(log2 n)

接下来统计一下每个 d [ h ] [ k ] d[h][k] d[h][k] 出现过多少次。 d [ 1 ] [ 1 ] d[1][1] d[1][1] 出现了 x × y xtimes y x×y次, d [ 1 ] [ 2 ] d[1][2] d[1][2]

现了 x × ( y − 1 ) xtimes(y-1) x×(y1)

……

d [ h ] [ k ] d[h][k] d[h][k]出现了 ( x − h + 1 ) ∗ ( y − k + 1 ) (x-h+1)*(y-k+1) (xh+1)(yk+1) 次。

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

∑ i = 1 x ∑ j = 1 y d [ i ] [ j ] × ( x + 1 − i ) ∗ ( y + 1 − j ) sum_{i=1}^{x}sum_{j=1}^{y}d[i][j]times(x+1-i)*(y+1-j) i=1xj=1yd[i][j]×(x+1i)(y+1j)

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

( x + 1 ) × ( y + 1 ) × ∑ i = 1 x ∑ j = 1 y d [ i ] [ j ] − ( y + 1 ) × ∑ i = 1 x ∑ j = 1 y d [ i ] [ j ] × i − ( x + 1 ) × ∑ i = 1 x ∑ j = 1 y d [ i ] [ j ] × j + ∑ i = 1 x ∑ j = 1 y d [ i ] [ j ] × i × j (x+1)times(y+1)timessum_{i=1}^{x}sum_{j=1}^{y}d[i][j]-(y+1)times sum_{i=1}^{x}sum_{j=1}^{y}d[i][j]times i-(x+1)times sum_{i=1}^{x}sum_{j=1}^{y}d[i][j]times j+sum_{i=1}^{x}sum_{j=1}^{y}d[i][j]times itimes j (x+1)×(y+1)×i=1xj=1yd[i][j](y+1)×i=1xj=1yd[i][j]×i(x+1)×i=1xj=1yd[i][j]×j+i=1xj=1yd[i][j]×i×j

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

d [ i ] [ j ] , d [ i ] [ j ] × i , d [ i ] [ j ] × j , d [ i ] [ j ] × i × j d[i][j],d[i][j]times i,d[i][j]times j,d[i][j]times itimes j d[i][j],d[i][j]×i,d[i][j]×j,d[i][j]×i×j

E N D mathcal{END} END

最后

以上就是传统玉米为你收集整理的树状数组详解 树 状 数 组 详 解 树状数组详解 树状数组详解 的全部内容,希望文章能够帮你解决树状数组详解 树 状 数 组 详 解 树状数组详解 树状数组详解 所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(41)

评论列表共有 0 条评论

立即
投稿
返回
顶部