题目链接:
传送门
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 500010;
int m, p, arr[N];
char op[10];
//结构体
struct Node {
int l, r;
int sum;
} tr[N * 4];
//pushup更新
void pushup(int u) {
tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
}
//创建线段树
void build(int u, int l, int r) {
//只指向了一个点
if (l == r) {
tr[u] = {l, r, arr[l]};
//不断往下二分
} else {
tr[u].l = l, tr[u].r = r;
int mid = l + r >> 1;
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
pushup(u);
}
}
//查询区间和
int query(int u, int l, int r) {
//当前区间是查询区间的子集
if (tr[u].l >= l && tr[u].r <= r) return tr[u].sum;
else {
int mid = tr[u].l + tr[u].r >> 1;
//查询区间在当前区间的左边
if (r <= mid) {
return query(u << 1, l, r);
//查询区间在当前区间的右边
} else if (l > mid) {
return query(u << 1 | 1, l, r);
//查询区间在当前区间的两边
} else {
int left = query(u << 1, l, r);
int right = query(u << 1 | 1, l, r);
return left + right;
}
}
}
//修改相应点的值
void modify(int u, int x, int val) {
if (tr[u].l == x && tr[u].r == x) tr[u].sum = val;
else {
int mid = tr[u].l + tr[u].r >> 1;
//查询到当前点在左边
if (x <= mid) modify(u << 1, x, val);
//查询到当前点在右边
else modify(u << 1 | 1, x, val);
pushup(u);
}
}
int main() {
int t, n, num1, num2, ca = 1;
scanf("%d", &t);
while(t--) {
memset(tr, 0, sizeof(tr));
memset(arr, 0, sizeof(arr));
scanf("%d", &n);
for(int i = 1; i <= n; i++)
scanf("%d", &arr[i]);
//创建线段树
build(1, 1, n);
printf("Case %d:\n",ca++);
//读入操作
while(scanf("%s", op)) {
//结束
if(op[0] == 'E') break;
//查询
else if(op[0] == 'Q') {
scanf("%d%d", &num1, &num2);
printf("%d\n", query(1, num1, num2));
//增添
} else if(op[0] == 'A') {
scanf("%d%d", &num1, &num2);
arr[num1] += num2;
modify(1, num1, arr[num1]);
//删减
} else {
scanf("%d%d", &num1, &num2);
arr[num1] -= num2;
modify(1, num1, arr[num1]);
}
}
}
}
这是一道线段树单点查询,区间修改的题目,线段树相关知识详情请见某大佬博客
这道题的基本思路就是维护一个求总和的线段树,显然pushup的更新方法此时就应该是tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum 即让父结点的值等于两个子节点的和,区间查询时,遇到分叉点则分别求两个分叉,再返回两个分叉的总和,其余的套模板就好,记得初始化的问题。