树状数组的三种代码模板

下标从一开始。

所有奇数等于本身,偶数/2等于所在层数。

二进制末尾有几个0就在第几层。

 

每一个树状数组中表示的都是这么一个区间的和,左开右闭。

 

写成lowbit(x),返回的就是2^k,k就是末尾有几个0。

 

这是求和代码

 

单点修改


 

这是单点修改,区间查询的代码:

#include<iostream>
#include<climits>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
using namespace std;
using ll = long long;
const int N = 1e5 + 9;
int n, m, a[N], tr[N];
// O(log2n)
// 树状数组解决:单点修改以及区间查询(常考),区间修改以及单点查询(配合差分转化为第一种情况),区间修改以及区间查询(配合差分转化为第一种情况)
// 注意下标一定要从1开始
// 树状树状的所有的奇数和原数组相等(树状数组的第0层)
// c[x]:x的二进制表示最后有k个连续的0,就在第k层
// c[x] 表示的和的范围:(x - 2^k, x],也就是(x - lowbit(x), x]
// lowbit(x) = x & -x = 2^k
// 所以要求x的前缀和需要递归求解:
// for (int i = 0; i>0; i -= lowbit(i)) res += c[i];
// 修改值后的前缀和:A[x] + v
// for (int i = x; i <= n; i += lowbit(i)) c[i] += v; 
// 上述两个循环时间复杂度都是O(log2n)

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

void add(int x, int v)
{
    for (int i = x; i <= n; i += lowbit(i)) tr[i] += v;
}

int query(int x)
{
    int res = 0;
    for (int i = x; i > 0; i -= lowbit(i)) res += tr[i];
    return res;
}


int main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    
    cin >> n >> m;
    
    for (int i = 1; i <= n; ++i) cin >> a[i];
    for (int i = 1; i <= n; ++i) add(i, a[i]);
    
    while (m--)
    {
        int k, x, y; cin >> k >> x >> y;
        
        if (k == 0) cout << query(y) - query(x - 1) << '\n';
        else add(x, y);// 在第x位置上加上一个y
    }
    
    return 0;
}



 

这是区间修改,单点查询的代码:

#include<iostream>
#include<climits>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#include<string>
#include<vector>
#include<tuple>
#include<set>
#include<bitset>
#include<unordered_map>
using namespace std;
using ll = long long;
using ull = unsigned long long;
const int N = 1e5 + 9, mod = 998244353, INF = 0x3f3f3f3f, M = 1, P = 13331;
const double eps = 1e-8;
#define x first
#define y second
typedef pair<int, int> PII;
int T, n, m, k, len, res, e[M], ne[M], h[N], w[M], idx, dist[N], f[N][2], a[N], r, c;

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

void add(int x, int v) {
    for (int i = x; i <= n; i += lowbit(i)) h[i] += v;
}

int query(int x) {
    int res = a[x];
    for (int i = x; i; i -= lowbit(i)) res += h[i];
    return res;
}

// 区间修改,单点查询

int main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    
    cin >> n >> m;
    
    for (int i = 1; i <= n; ++i) cin >> a[i];
    
    char op;
    while (m--) {
        cin >> op;
        if (op == 'Q') {
            int x; cin >> x;
            cout << query(x) << '\n';
        }
        
        else {
            int l, r, v; cin >> l >> r >> v;
            add(l, v), add(r + 1, -v);
        }
    }
    
    return 0;
}

区间修改,区间查询:

 

#include<iostream>
#include<climits>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#include<string>
#include<vector>
#include<tuple>
#include<set>
#include<bitset>
#include<unordered_map>
using namespace std;
using ll = long long;
using ull = unsigned long long;
const int N = 2e5 + 9, mod = 998244353, INF = 0x3f3f3f3f, M = 1, P = 13331;
const double eps = 1e-8;
#define x first
#define y second
typedef pair<int, int> PII;
ll T, n, m, k, len, res, e[M], ne[M], h[N], w[M], idx, dist[N], f[N][2], a[N], r, c;
ll c1[N], c2[N];


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

void add(ll x, ll v) {
    for (int i = x; i <= n; i += lowbit(i)) {
        c1[i] += v;
        c2[i] += x * v;
    }
}

ll query(ll x) {
    ll res = 0;
    for (int i = x; i; i -= lowbit(i)) res += (x + 1) * c1[i] - c2[i];
    return res;
}

int main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);

    cin >> n >> m;

    for (int i = 1; i <= n; ++i) {
        cin >> a[i];
        add(i, a[i] - a[i - 1]);
    }

    char op;
    while (m--) {
        cin >> op;
        if (op == 'Q') {
            ll l, r; cin >> l >> r;
            cout << query(r) - query(l - 1) << '\n';
        }

        else {
            ll l, r, v; cin >> l >> r >> v;
            add(l, v), add(r + 1, -v);
        }
    }

    return 0;
}
  • 10
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值