【数据结构】树状数组
单点修改,区间求和
模板
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;
}
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;
}
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;
}
进一步解释:
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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架