【数据结构】树状数组

单点修改,区间求和

模板

template<typename T>
class FenwickTree {
private:
    int n;              // 数组元素个数(下标从1开始)
    vector<T> tree;     // 树状数组存储

    // 获取x的二进制最低位的1对应的值
    int lowbit(int x) {
        return x & -x;
    }

public:
    /* 构造函数
     * 参数:数据数组(下标从1开始)
     * 示例:FenwickTree<int> ft(vector<int>(n+1));
     */
    FenwickTree(const vector<T>& data) : n(data.size() - 1) {
        tree.resize(n + 1, 0);
        for (int i = 1; i <= n; ++i) {
            update(i, data[i]);
        }
    }

    /* 单点更新:给位置i增加val
     * 参数:i为下标(1-based),val为增量
     */
    void update(int i, T val) {
        while (i <= n) {
            tree[i] += val;
            i += lowbit(i);
        }
    }

    /* 前缀和查询:求[1, i]的和
     * 参数:i为下标(1-based)
     */
    T query(int i) {
        T sum = 0;
        while (i > 0) {
            sum += tree[i];
            i -= lowbit(i);
        }
        return sum;
    }

    /* 区间和查询:求[l, r]的和
     * 参数:l和r为下标(1-based)
     */
    T range_query(int l, int r) {
        return query(r) - query(l - 1);
    }
    
    // 以下函数为扩展功能
   	/* 把某个位置的值改为val
   		通过计算差值进行更新
   		原来i位置的数是x, 通过update让x + (val - x) = val
   	 */
    void set(int i, T new_val) {
    	T old_val = range_query(i, i);
    	update(i, new_val - old_val);
    }
};

使用示例

int main() {
    // 初始化(注意数据容器从1开始)
    vector<int> data{0, 1, 3, 5, 7, 9}; // [0,1,3,5,7,9]
    FenwickTree<int> ft(data);

    cout << ft.query(3) << endl;     // 输出1+3+5=9
    cout << ft.range_query(2, 4) << endl; // 3+5+7=15

    ft.update(2, 4); // 位置2加4 → 数据变为[0,1,7,5,7,9]
    cout << ft.query(3) << endl;     // 1+7+5=13

    return 0;
}

image

image

image


P3374 【模板】树状数组 1

数组简洁版

#include <iostream>
#include <vector>

using namespace std;
using ll = long long;

const int N = 5e5 + 10;

int n, m, op, x, y;
ll k;
ll t[N];

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

void update(int i, ll val)
{
    while (i <= n)
    {
        t[i] += val;
        i += lowbit(i);
    }
}

ll query(int i)
{
    ll sum = 0;
    while (i > 0)
    {
        sum += t[i];
        i -= lowbit(i);
    }
    return sum;
}

void solve()
{
    cin >> n >> m;
    for (int i = 1; i <= n; i++)
    {
        ll x;
        cin >> x;
        update(i, x);
    }
    while (m--)
    {
        cin >> op >> x;
        if (op == 1)
        {
            cin >> k;
            update(x, k);
        }
        else
        {
            cin >> y;
            cout << query(y) - query(x - 1) << '\n';
        }
    }
}

int main()
{
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int T = 1;
    while (T--) solve();
    return 0;
}

直接套板子

// Problem: P3374 【模板】树状数组 1
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P3374
// Memory Limit: 512 MB
// Time Limit: 1000 ms

#include <iostream>
#include <vector>

using namespace std;
using ll = long long;

template <typename T>
class FenwickTree
{
private:
    int n;           // 数组元素个数(下标从1开始)
    vector<T> tree;  // 树状数组存储

    // 获取x的二进制最低位的1对应的值
    int lowbit(int x)
    {
        return x & -x;
    }

public:
    /* 构造函数
     * 参数:数据数组(下标从1开始)
     * 示例:FenwickTree<int> ft(vector<int>(n+1));
     */
    FenwickTree(const vector<T>& data) : n(data.size() - 1)
    {
        tree.resize(n + 1, 0);
        for (int i = 1; i <= n; ++i)
        {
            update(i, data[i]);
        }
    }

    /* 单点更新:给位置i增加val
     * 参数:i为下标(1-based),val为增量
     */
    void update(int i, T val)
    {
        while (i <= n)
        {
            tree[i] += val;
            i += lowbit(i);
        }
    }

    /* 前缀和查询:求[1, i]的和
     * 参数:i为下标(1-based)
     */
    T query(int i)
    {
        T sum = 0;
        while (i > 0)
        {
            sum += tree[i];
            i -= lowbit(i);
        }
        return sum;
    }

    /* 区间和查询:求[l, r]的和
     * 参数:l和r为下标(1-based)
     */
    T range_query(int l, int r)
    {
        return query(r) - query(l - 1);
    }

    // 以下两个函数为扩展功能
    /* 区间加减值:令区间[l, r] + val
       原理是差分。不能直接用,如果要用,树状数组里应该存差分数组
     */
    void add_range(int l, int r, T val)
    {
        update(l, val);
        update(r + 1, -val);
    }
    /* 把某个位置的值改为val
            通过计算差值进行更新
            原来i位置的数是x, 通过update让x + (val - x) = val
     */
    void set(int i, T new_val)
    {
        T old_val = range_query(i, i);
        update(i, new_val - old_val);
    }
};

void solve()
{
    int n, m, op;
    ll x, y, k;
    cin >> n >> m;

    vector<ll> a(n + 1);
    for (int i = 1; i <= n; i++) cin >> a[i];

    FenwickTree<ll> tr(a);
    while (m--)
    {
        cin >> op >> x;
        if (op == 1)
        {
            cin >> k;
            tr.update(x, k);
        }
        else
        {
            cin >> y;
            cout << tr.range_query(x, y) << '\n';
        }
    }
}

int main()
{
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int T = 1;
    while (T--) solve();
    return 0;
}

区间修改,单点求和(查询)(差分思想,树状数组中存的是差分数组,不是原数组,这样对差分数组求前缀和就得到了原数组)

板子

template<typename T>
class FenwickTree {
private:
    int n;              // 数组大小(下标从1开始)
    vector<T> tree;     // 维护差分数组的前缀和

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

public:
    // 构造函数(输入原数组)
    FenwickTree(const vector<T>& a) : n(a.size() - 1) {
        tree.resize(n + 1, 0);
        for (int i = 1; i <= n; ++i) {
            T diff = (i == 1) ? a[i] : a[i] - a[i-1];
            update(i, diff);
        }
    }

    // 单点更新:给差分数组d[i]增加val
    void update(int i, T val) {
        while (i <= n) {
            tree[i] += val;
            i += lowbit(i);
        }
    }

    // 前缀和查询:求a[i] = d[1] + d[2] + ... + d[i]
    T query(int i) {
        T sum = 0;
        while (i > 0) {
            sum += tree[i];
            i -= lowbit(i);
        }
        return sum;
    }

    // 区间修改:给a[l..r]增加val
    void add_range(int l, int r, T val) {
        update(l, val);
        if (r + 1 <= n) update(r + 1, -val);
    }

    // 单点查询:返回a[i]的当前值
    T get(int i) {
        return query(i);
    }
};

使用示例

#include <iostream>
int main() {
    // 原数组(下标从1开始)
    vector<int> a = {0, 3, 1, 4, 1, 5}; // [3,1,4,1,5]
    FenwickTree<int> ft(a);

    cout << ft.get(3) << endl; // 输出原值:4

    // 区间[2,4]加2 → 新数组[3,3,6,3,5]
    ft.add_range(2, 4, 2);
    cout << ft.get(3) << endl; // 输出6(原4+2=6)
    cout << ft.get(5) << endl; // 输出5(未受影响)

    // 单点查询
    cout << ft.get(4) << endl; // 输出3(原1+2=3)

    return 0;
}

image

image

image

P3368 【模板】树状数组 2

区间修改,单点查询(差分思想)

// Problem: P3368 【模板】树状数组 2
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P3368
// Memory Limit: 125 MB
// Time Limit: 1000 ms

#include <iostream>
#include <vector>

using namespace std;
using ll = long long;

#include <vector>
using namespace std;

template <typename T>
class FenwickTree
{
private:
    int n;           // 数组大小(下标从1开始)
    vector<T> tree;  // 维护差分数组的前缀和

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

public:
    // 构造函数(输入原数组)
    FenwickTree(const vector<T>& a) : n(a.size() - 1)
    {
        tree.resize(n + 1, 0);
        for (int i = 1; i <= n; ++i)
        {
            T diff = (i == 1) ? a[i] : a[i] - a[i - 1];
            update(i, diff);
        }
    }

    // 单点更新:给差分数组d[i]增加val
    void update(int i, T val)
    {
        while (i <= n)
        {
            tree[i] += val;
            i += lowbit(i);
        }
    }

    // 前缀和查询:求a[i] = d[1] + d[2] + ... + d[i]
    T query(int i)
    {
        T sum = 0;
        while (i > 0)
        {
            sum += tree[i];
            i -= lowbit(i);
        }
        return sum;
    }

    // 区间修改:给a[l..r]增加val
    void add_range(int l, int r, T val)
    {
        update(l, val);
        if (r + 1 <= n) update(r + 1, -val);
    }

    // 单点查询:返回a[i]的当前值
    T get(int i)
    {
        return query(i);
    }
};

void solve()
{
    int n, m, op, x, y;
    ll k;
    cin >> n >> m;

    vector<ll> a(n + 1);
    for (int i = 1; i <= n; i++) cin >> a[i];

    FenwickTree<ll> tr(a);
    while (m--)
    {
        cin >> op >> x;
        if (op == 1)
        {
            cin >> y >> k;
            tr.add_range(x, y, k);
        }
        else
        {
            cout << tr.get(x) << '\n';
        }
    }
}

int main()
{
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int T = 1;
    while (T--) solve();
    return 0;
}

区间修改,区间求和(线段树的功能,但树状数组不能求区间最值)

板子

template<typename T>
class RangeFenwickTree {
private:
    int n;
    vector<T> tree1;  // 维护 d[i]
    vector<T> tree2;  // 维护 d[i] * i

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

    void update(vector<T>& tree, int i, T val) {
        while (i <= n) {
            tree[i] += val;
            i += lowbit(i);
        }
    }

    T query(const vector<T>& tree, int i) {
        T sum = 0;
        while (i > 0) {
            sum += tree[i];
            i -= lowbit(i);
        }
        return sum;
    }

public:
    // 构造函数(数组下标从1开始)
    RangeFenwickTree(int size) : n(size) {
        tree1.resize(n + 1, 0);
        tree2.resize(n + 1, 0);
    }

    // 区间 [l, r] 增加 val
    void add_range(int l, int r, T val) {
        update(tree1, l, val);
        update(tree1, r + 1, -val);
        update(tree2, l, val * l);
        update(tree2, r + 1, -val * (r + 1));
    }

    // 查询前缀和 [1, i]
    T query_prefix(int i) {
        return (i + 1) * query(tree1, i) - query(tree2, i);
    }

    // 查询区间和 [l, r]
    T query_range(int l, int r) {
        return query_prefix(r) - query_prefix(l - 1);
    }
};

使用示例

int main() {
    int n = 5;
    RangeFenwickTree<long long> ft(n);

    // 初始数组全为0
    ft.add_range(1, 3, 5);    // [5,5,5,0,0]
    ft.add_range(2, 4, 3);    // [5,8,8,3,0]

    cout << ft.query_range(1, 3) << endl; // 5+8+8=21
    cout << ft.query_range(2, 4) << endl; // 8+8+3=19
    cout << ft.query_range(3, 5) << endl; // 8+3+0=11

    return 0;
}

image

image

进一步解释:
image

image

image

image

image

image

P3372 【模板】线段树 1

// Problem: P3372 【模板】线段树 1
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P3372
// Memory Limit: 512 MB
// Time Limit: 1000 ms

#include <iostream>
#include <vector>

using namespace std;
using ll = long long;

template <typename T>
class RangeFenwickTree
{
private:
    int n;
    vector<T> tree1;  // 维护 d[i]
    vector<T> tree2;  // 维护 d[i] * i

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

    void update(vector<T>& tree, int i, T val)
    {
        while (i <= n)
        {
            tree[i] += val;
            i += lowbit(i);
        }
    }

    T query(const vector<T>& tree, int i)
    {
        T sum = 0;
        while (i > 0)
        {
            sum += tree[i];
            i -= lowbit(i);
        }
        return sum;
    }

public:
    // 构造函数(数组下标从1开始)
    RangeFenwickTree(int size) : n(size)
    {
        tree1.resize(n + 1, 0);
        tree2.resize(n + 1, 0);
    }

    // 区间 [l, r] 增加 val
    void add_range(int l, int r, T val)
    {
        update(tree1, l, val);
        update(tree1, r + 1, -val);
        update(tree2, l, val * l);
        update(tree2, r + 1, -val * (r + 1));
    }

    // 查询前缀和 [1, i]
    T query_prefix(int i)
    {
        return (i + 1) * query(tree1, i) - query(tree2, i);
    }

    // 查询区间和 [l, r]
    T query_range(int l, int r)
    {
        return query_prefix(r) - query_prefix(l - 1);
    }
};

void solve()
{
    int n, m, op, x, y;
    ll k;
    cin >> n >> m;

    RangeFenwickTree<ll> tr(n + 1);
    for (int i = 1; i <= n; i++)
    {
        ll x;
        cin >> x;
        tr.add_range(i, i, x);
    }

    while (m--)
    {
        cin >> op >> x >> y;
        if (op == 1)
        {
            cin >> k;
            tr.add_range(x, y, k);
        }
        else
        {
            cout << tr.query_range(x, y) << '\n';
        }
    }
}

int main()
{
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int T = 1;
    while (T--) solve();
    return 0;
}

P3368 【模板】树状数组 2

区间修改,区间查询(含单点查询,把左右端点写成同一个就是单点查询了)

// Problem: P3368 【模板】树状数组 2
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P3368
// Memory Limit: 125 MB
// Time Limit: 1000 ms

#include <iostream>
#include <vector>

using namespace std;
using ll = long long;

template <typename T>
class RangeFenwickTree
{
private:
    int n;
    vector<T> tree1;  // 维护 d[i]
    vector<T> tree2;  // 维护 d[i] * i

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

    void update(vector<T>& tree, int i, T val)
    {
        while (i <= n)
        {
            tree[i] += val;
            i += lowbit(i);
        }
    }

    T query(const vector<T>& tree, int i)
    {
        T sum = 0;
        while (i > 0)
        {
            sum += tree[i];
            i -= lowbit(i);
        }
        return sum;
    }

public:
    // 构造函数(数组下标从1开始)
    RangeFenwickTree(int size) : n(size)
    {
        tree1.resize(n + 1, 0);
        tree2.resize(n + 1, 0);
    }

    // 区间 [l, r] 增加 val
    void add_range(int l, int r, T val)
    {
        update(tree1, l, val);
        update(tree1, r + 1, -val);
        update(tree2, l, val * l);
        update(tree2, r + 1, -val * (r + 1));
    }

    // 查询前缀和 [1, i]
    T query_prefix(int i)
    {
        return (i + 1) * query(tree1, i) - query(tree2, i);
    }

    // 查询区间和 [l, r]
    T query_range(int l, int r)
    {
        return query_prefix(r) - query_prefix(l - 1);
    }
};

void solve()
{
    int n, m, op, x, y;
    ll k;
    cin >> n >> m;

    RangeFenwickTree<ll> tr(n + 1);
    for (int i = 1; i <= n; i++)
    {
        ll x;
        cin >> x;
        tr.add_range(i, i, x);
    }
    while (m--)
    {
        cin >> op >> x;
        if (op == 1)
        {
            cin >> y >> k;
            tr.add_range(x, y, k);
        }
        else
        {
            cout << tr.query_range(x, x) << '\n';
        }
    }
}

int main()
{
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int T = 1;
    while (T--) solve();
    return 0;
}
posted @   Tshaxz  阅读(3)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架
Language: HTML
点击右上角即可分享
微信分享提示