zoukankan      html  css  js  c++  java
  • 树状数组

    题目 树状数组模板1 题目大意:给定一个序列,要求支持两种操作:1.将某个数加上 (x),2.查询区间某部分的和。

    题目 树状数组模板2 题目大意:给定一个序列,要求支持两种操作:1.将区间内的每个数加上 (x),2.查询某个数。

    分析 由于第一题与第二题实际上是等价的(稍后会说明),我们以下仅讨论第一题。我们先以最简单的数组来考虑,显然操作1是 (O(1)),操作2是 (O(n)) 的,总体复杂度 (O(nm)),很明显会超时。这时我们就会想到用前缀和来优化操作2,这样的话操作2是 (O(1)) 的,但是操作1是 (O(n)) 的,总体复杂度仍为 (O(nm)),会超时。有没有一种数据结构,可以使操作1和操作2都是 (O(1)) 的呢?仅目前来看是不存在的。于是我们退而求其次寻找一种数据结构,使操作1和操作2都是 (O(log n)) 的。

    树状数组(又称二叉索引树,Binary Indexed Tree,BIT)是一类最基础的树形数据结构。它支持 (O(log n)) 进行区间修改单点查询或区间查询单点修改,亦即上述两道题的操作。具体来说,树状数组自身本质上仍是一个数组。我们令 (a) 为原数组,(c) 为树状数组,假定 (i) 二进制最后有 (j) 个0,则有 (c_i=sum_{k=0}^{2^j}a_{i-k})

    树状数组
    (图片来自网络,侵删)

    不难发现,现在我们更改与查询的时候只需更改包含着对象的位置就可以了,这样的位置不会超过 (log n) 个(为什么),所以我们只需 (O(1)) 从一个位置找到包含原对象的另一个位置就行了。

    如果当前要修改 (1(0001)),则要修改 (1(0001),2(0010),4(0100),8(1000))

    如果当前要修改 (5(0101)),则要修改 (5(0101),6(0110),8(1000))

    如果当前要查询 (7(0111)) 的前缀和,则要查询 (7(0111),6(0110),4(0100))

    如果当前要查询 (5(0101)) 的前缀和,则要查询 (5(0101),4(0100))

    不难发现,修改一个数时,下一个要修改的目标位置就是当前位置加上当前位置的最后一位,如 (6 ightarrow 8(0110+0010=1000));查询一个数时,下一个要修改的目标位置就是当前位置减去当前位置的最后一位,如 (5 ightarrow 4(0101-0001=0100))。那么现在问题转化为了如何 (O(1)) 求出某个数二进制最后一位,那便是lowbit函数:

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

    这个函数可以以 (O(1)) 的优秀复杂度求出某个数二进制的最后一位(比如 (3(0011) ightarrow 1(0001),6(0110) ightarrow 2(0010))),证明只需一点点基础的二进制编码,留给读者自证。

    那么现在我们就可以愉快地写出第一道题的代码。而对于第二题,不难发现,只要对原数列进行差分,就可以将区间修改转变为单点修改,单点查询转变为区间查询。

    代码
    第一题:

    #include<bits/stdc++.h>
    using namespace std;
    
    const int maxn = 5E+5 + 5;
    
    int n, m;
    int c[maxn];
    
    int Read()
    {
        int x = 0, op = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9') {
            if(ch == '-') op = -1;
            ch = getchar();
        }
        while(ch >= '0' && ch <= '9') {
            x = (x << 3) + (x << 1) + (ch - '0');
            ch = getchar();
        }
        return x * op;
    }
    
    inline int lowbit(int x) { return x & (-x); }
    inline void Add(int pos, int x) { while(pos <= n) c[pos] += x, pos += lowbit(pos); }
    
    int Query(int pos)
    {
        int res = 0;
        while(pos) {
            res += c[pos];
            pos -= lowbit(pos);
        }
        return res;
    }
    
    int main()
    {
        scanf("%d%d", &n, &m);
        for(int i = 1; i <= n; ++i) Add(i, Read());
    
        while(m--) {
            int op = Read(), x = Read(), y = Read();
    
            if(op == 1) Add(x,y);
            else printf("%d
    ", Query(y) - Query(x - 1));
        }
    }
    

    第二题:

    #include<bits/stdc++.h>
    using namespace std;
    
    const int maxn = 5E+5 + 5;
    
    int n, m;
    int a[maxn], c[maxn];
    
    int Read()
    {
        int x = 0, op = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9') {
            if(ch == '-') op = -1;
            ch = getchar();
        }
        while(ch >= '0' && ch <= '9') {
            x = (x << 3) + (x << 1) + (ch - '0');
            ch = getchar();
        }
        return x * op;
    }
    
    inline int lowbit(int x) { return x & (-x); }
    inline void Add(int pos, int x) { while(pos <= n) c[pos] += x, pos += lowbit(pos); }
    
    int Query(int pos)
    {
        int res = 0;
        while(pos) {
            res += c[pos];
            pos -= lowbit(pos);
        }
        return res;
    }
    
    int main()
    {
        scanf("%d%d", &n, &m);
        for(int i = 1; i <= n; ++i) a[i] = Read();
    
        while(m--) {
            int op = Read(), x = Read(), y, z;
    
            if(op == 1) y = Read(), z = Read(), Add(x, z), Add(y + 1, -z);
            else printf("%d
    ", a[x] + Query(x));
        }
    }
    
  • 相关阅读:
    BZOJ 3744 Gty的妹子序列
    BZOJ 3872 Ant colony
    BZOJ 1087 互不侵犯
    BZOJ 1070 修车
    BZOJ 2654 tree
    BZOJ 3243 向量内积
    1003 NOIP 模拟赛Day2 城市建设
    CF865D Buy Low Sell High
    CF444A DZY Loves Physics
    Luogu 4310 绝世好题
  • 原文地址:https://www.cnblogs.com/whx1003/p/11828666.html
Copyright © 2011-2022 走看看