一、题目链接
二、题目分析
(一)算法标签
树状数组 线段树
(二)解题思路
1. 树状数组
操作1:
单点修改
O
(
l
o
g
n
)
O(logn)
O(logn)
操作2:
区间查询
O
(
l
o
g
n
)
O(logn)
O(logn)
三个核心函数:
1. int lowbit(int x);
int lowbit(int x)
{
return x & -x;
}
2. void update(int x, int c) // 位置x加c
void update(int x, int c) // 位置x加c
{
for (int i = x; i <= n; i += lowbit(i)) tr[i] += c;
}
3. int query(int x) // 返回前x个数的和
int query(int x) // 返回前x个数的和
{
int res = 0;
for (int i = x; i; i -= lowbit(i)) res += tr[i];
return res;
}
2. 线段树
操作1:
单点修改
O
(
l
o
g
n
)
O(logn)
O(logn)
操作2:
区间查询
O
(
l
o
g
n
)
O(logn)
O(logn)
数据结构:
struct Node
{
int l, r;
int sum;
}tr[N * 4];
核心函数:
1. void pushup(int u); // 利用左右儿子信息维护当前节点的信息
void pushup(int u)
{
// TODO: 利用左右儿子信息维护当前节点的信息
tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
}
2. void build(int u, int l, int r); // u为根结点编号,初始化结点u维护的区间为[l, r]
- 如果区间长度
为1
(即l == r),则初始化当前结点- 如果区间长度
不为1
:
- 初始化当前结点(维护哪一段区间),
- 再分别
左右递归
初始化左子线段树
和右子线段树
- 初始化完后利用
pushup()
函数更新父节点信息
void build(int u, int l, int r)
{
if (l == r) tr[u] = {l, r, w[r]}; // w[i]表示权值
else
{
tr[u] = {l, r};
int mid = l + r >> 1;
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
pushup(u);
}
}
3. void update(int u, int x, int v); // u为根结点编号,在x位置上的数加上v
- 如果u是
叶子结点
,直接加上v即可- 否则: 计算mid
x <= mid
时更新左子树
x > mid
时更新右子树
- 利用
pushup()
函数更新父节点信息
void update(int u, int x, int v)
{
// 如果是叶子结点,直接加上v即可
if (tr[u].l == tr[u].r) tr[u].sum += v;
// 否则,递归左右子树:x <= mid时更新左子树, x > mid时更新右子树; 更新完后pushup()更新父节点信息
else
{
int mid = tr[u].l + tr[u].r >> 1;
if (x <= mid) update(u << 1, x, v);
else update(u << 1 | 1, x, v);
pushup(u);
}
}
4. int query(int u, int l, int r);
- 如果当前结点所维护的区间在[l, r]中, 直接return结点u的sum值
- 否则:计算mid, 初始化sum = 0
- 若l <= mid(mid 左边还有要加和的数) ,递归左子树
- 若r > mid(mid 右边还有要加和的数) ,递归右子树
- 返回sum
int query(int u, int l, int r)
{
// 如果当前结点所维护的区间在[l, r]中, 直接return结点u的sum值
if (tr[u].l >= l && tr[u].r <= r) return tr[u].sum;
// 否则:计算mid, 初始化sum = 0
else
{
int mid = tr[u].l + tr[u].r >> 1;
int sum = 0;
// 若l <= mid(mid 左边还有要加和的数) ,递归左子树
if (l <= mid) sum = query(u << 1, l, r);
// 若r > mid(mid 右边还有要加和的数) ,递归右子树
if (r > mid) sum += query(u << 1 | 1, l, r);
// 返回sum
return sum;
}
}
三、AC代码
解法一:(树状数组)
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 100010;
int n, m;
int a[N], tr[N];
int lowbit(int x)
{
return x & -x;
}
void update(int x, int c) // 位置x加c
{
for (int i = x; i <= n; i += lowbit(i)) tr[i] += c;
}
int query(int x) // 返回前x个数的和
{
int res = 0;
for (int i = x; i; i -= lowbit(i)) res += tr[i];
return res;
}
int main()
{
cin >> n >> m;
for (int i = 1; i <= n; i ++ ) scanf("%d", &a[i]);
// 初始化树状数组
for (int i = 1; i <= n; i ++ ) update(i, a[i]);
while (m -- )
{
int k, x, y;
scanf("%d%d%d", &k, &x, &y);
if (k == 0) printf("%d\n", query(y) - query(x - 1));
else
update(x, y);
}
return 0;
}
解法二:(线段树)
#include <iostream>
using namespace std;
const int N = 1e5 + 10;
int n, m;
int w[N];
struct Node{
int l, r, sum;
}tr[4 * N];
void pushup(int u) // 利用左右儿子信息维护当前节点的信息
{
tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
}
void build(int u, int l, int r) // u为根结点编号,初始化结点u维护的区间为[l, r]
{
if (l == r) tr[u] = {l, r, w[r]}; // w[i]表示权值
else
{
tr[u] = {l, r};
int mid = l + r >> 1;
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
pushup(u);
}
}
void update(int u, int x, int v)
{
// 如果是叶子结点,直接加上v即可
if (tr[u].l == tr[u].r) tr[u].sum += v;
// 否则,递归左右子树:x <= mid时更新左子树, x > mid时更新右子树; 更新完后pushup()更新父节点信息
else
{
int mid = tr[u].l + tr[u].r >> 1;
if (x <= mid) update(u << 1, x, v);
else update(u << 1 | 1, x, v);
pushup(u);
}
}
int query(int u, int l, int r)
{
// 如果当前结点所维护的区间在[l, r]中, 直接return结点u的sum值
if (tr[u].l >= l && tr[u].r <= r) return tr[u].sum;
// 否则:计算mid, 初始化sum = 0
else
{
int mid = tr[u].l + tr[u].r >> 1;
int sum = 0;
// 若l <= mid(mid 左边还有要加和的数) ,递归左子树
if (mid >= l) sum = query(u << 1, l, r);
// 若r > mid(mid 右边还有要加和的数) ,递归右子树
if (mid < r) sum += query(u << 1 | 1, l, r);
// 返回sum
return sum;
}
}
int main()
{
cin >> n >> m;
for (int i = 1; i <= n; i ++ ) scanf("%d", &w[i]);
build(1, 1, n);
while (m -- )
{
int k, a, b;
scanf("%d%d%d", &k, &a, &b);
if (k == 0) printf("%d\n", query(1, a, b));
else update(1, a, b);
}
return 0;
}