简介
线段树和树状数组这两种数据结构都能处理区间修改、区间查询问题。与树状数组相比线段树有以下特点。
(1)线段树的原理。线段树的原理比树状数组直观,在理论上它比树状数组更好懂,但是线段树的细节很多,应用起来比较烦琐。
(2)线段树的代码。线段树要处理大量细节,代码冗长。一个与线段树有关的题目,基本的线段树那一部分的代码约 40 行,而树状数组的核心代码不到 10 行。
但是线段树比树状数组更通用。很多问题用线段树解决非常直观,线段树比树状数组的应用场合多。做题的时候,如果一道题同时能用线段树和树状数组求解,大多数人会选择用代码多的线段树,而不是代码少的树状数组。因为树状数组的建模、逻辑比线段树的复杂。
例题
代码
package aaa;
import java.util.Scanner;
public class test_5 {
static int N = 200001;
static int[] tree = new int[N << 2];
public static void main(String[] args) {
int t = 0, cnt = 0, m, D;
Scanner scanner = new Scanner(System.in);
m = scanner.nextInt();
D = scanner.nextInt();
update(1, 1, N, 1, N, Integer.MIN_VALUE);
for (int b = 1; b <= m; b++) {
String s = scanner.next();
int x = scanner.nextInt();
if (s.charAt(0) == 'A') {
cnt++;
update(1, 1, N, cnt, cnt, (x + t) % D);
} else {
t = query(1, 1, N, cnt - x + 1, cnt);
System.out.println(t);
}
}
}
public static int ls(int p) { // 左子节点
return p << 1;
}
public static int rs(int p) { // 右子节点
return p << 1 + 1;
}
public static void pushUp(int p) { // 从下往上传递区间值
tree[p] = Math.max(tree[ls(p)], tree[rs(p)]);
}
public static void build(int p, int pl, int pr) {
System.out.println(p);
if (pl == pr) {
tree[p] = Integer.MIN_VALUE;
return;
}
int mid = (pl + pr) >> 1;
build(ls(p), pl, mid); //构建左子树
build(rs(p), mid + 1, pr); //构建右子树
pushUp(p);
}
public static void update(int p, int pl, int pr, int L, int R, int d) {
if (L <= pl && pr <= R) {
tree[p] = d;
return;
}
int mid = (pl + pr) >> 1;
if (L <= mid) {
update(ls(p), pl, mid, L, R, d);
}
if (R > mid) {
update(rs(p), mid + 1, pr, L, R, d);
}
pushUp(p);
}
public static int query(int p, int pl, int pr, int L, int R) {
int res = Integer.MIN_VALUE;
if (L <= pl & pr <= R) {
return tree[p];
}
int mid = (pl + pr) >> 1;
if (L <= mid)
res = Math.max(query(ls(p), pl, mid, L, R), res);
if (R > mid)
res = Math.max(res, query(rs(p), mid + 1, pr, L, R));
return res;
}
}