|
|
package class31;
|
|
|
|
|
|
public class Code01_SegmentTree {
|
|
|
|
|
|
public static class SegmentTree {
|
|
|
// arr[]为原序列的信息从0开始,但在arr里是从1开始的
|
|
|
// sum[]模拟线段树维护区间和
|
|
|
// lazy[]为累加和懒惰标记
|
|
|
// change[]为更新的值
|
|
|
// update[]为更新慵懒标记
|
|
|
private int MAXN;
|
|
|
private int[] arr;
|
|
|
private int[] sum;
|
|
|
private int[] lazy;
|
|
|
private int[] change;
|
|
|
private boolean[] update;
|
|
|
|
|
|
public SegmentTree(int[] origin) {
|
|
|
MAXN = origin.length + 1;
|
|
|
arr = new int[MAXN]; // arr[0] 不用 从1开始使用
|
|
|
for (int i = 1; i < MAXN; i++) {
|
|
|
arr[i] = origin[i - 1];
|
|
|
}
|
|
|
sum = new int[MAXN << 2]; // 用来支持脑补概念中,某一个范围的累加和信息
|
|
|
lazy = new int[MAXN << 2]; // 用来支持脑补概念中,某一个范围沒有往下傳遞的纍加任務
|
|
|
change = new int[MAXN << 2]; // 用来支持脑补概念中,某一个范围有没有更新操作的任务
|
|
|
update = new boolean[MAXN << 2]; // 用来支持脑补概念中,某一个范围更新任务,更新成了什么
|
|
|
}
|
|
|
|
|
|
private void pushUp(int rt) {
|
|
|
sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
|
|
|
}
|
|
|
|
|
|
// 之前的,所有懒增加,和懒更新,从父范围,发给左右两个子范围
|
|
|
// 分发策略是什么
|
|
|
// ln表示左子树元素结点个数,rn表示右子树结点个数
|
|
|
private void pushDown(int rt, int ln, int rn) {
|
|
|
if (update[rt]) {
|
|
|
update[rt << 1] = true;
|
|
|
update[rt << 1 | 1] = true;
|
|
|
change[rt << 1] = change[rt];
|
|
|
change[rt << 1 | 1] = change[rt];
|
|
|
lazy[rt << 1] = 0;
|
|
|
lazy[rt << 1 | 1] = 0;
|
|
|
sum[rt << 1] = change[rt] * ln;
|
|
|
sum[rt << 1 | 1] = change[rt] * rn;
|
|
|
update[rt] = false;
|
|
|
}
|
|
|
if (lazy[rt] != 0) {
|
|
|
lazy[rt << 1] += lazy[rt];
|
|
|
sum[rt << 1] += lazy[rt] * ln;
|
|
|
lazy[rt << 1 | 1] += lazy[rt];
|
|
|
sum[rt << 1 | 1] += lazy[rt] * rn;
|
|
|
lazy[rt] = 0;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 在初始化阶段,先把sum数组,填好
|
|
|
// 在arr[l~r]范围上,去build,1~N,
|
|
|
// rt : 这个范围在sum中的下标
|
|
|
public void build(int l, int r, int rt) {
|
|
|
if (l == r) {
|
|
|
sum[rt] = arr[l];
|
|
|
return;
|
|
|
}
|
|
|
int mid = (l + r) >> 1;
|
|
|
build(l, mid, rt << 1);
|
|
|
build(mid + 1, r, rt << 1 | 1);
|
|
|
pushUp(rt);
|
|
|
}
|
|
|
|
|
|
|
|
|
// L~R 所有的值变成C
|
|
|
// l~r rt
|
|
|
public void update(int L, int R, int C, int l, int r, int rt) {
|
|
|
if (L <= l && r <= R) {
|
|
|
update[rt] = true;
|
|
|
change[rt] = C;
|
|
|
sum[rt] = C * (r - l + 1);
|
|
|
lazy[rt] = 0;
|
|
|
return;
|
|
|
}
|
|
|
// 当前任务躲不掉,无法懒更新,要往下发
|
|
|
int mid = (l + r) >> 1;
|
|
|
pushDown(rt, mid - l + 1, r - mid);
|
|
|
if (L <= mid) {
|
|
|
update(L, R, C, l, mid, rt << 1);
|
|
|
}
|
|
|
if (R > mid) {
|
|
|
update(L, R, C, mid + 1, r, rt << 1 | 1);
|
|
|
}
|
|
|
pushUp(rt);
|
|
|
}
|
|
|
|
|
|
// L~R, C 任务!
|
|
|
// rt,l~r
|
|
|
public void add(int L, int R, int C, int l, int r, int rt) {
|
|
|
// 任务如果把此时的范围全包了!
|
|
|
if (L <= l && r <= R) {
|
|
|
sum[rt] += C * (r - l + 1);
|
|
|
lazy[rt] += C;
|
|
|
return;
|
|
|
}
|
|
|
// 任务没有把你全包!
|
|
|
// l r mid = (l+r)/2
|
|
|
int mid = (l + r) >> 1;
|
|
|
pushDown(rt, mid - l + 1, r - mid);
|
|
|
// L~R
|
|
|
if (L <= mid) {
|
|
|
add(L, R, C, l, mid, rt << 1);
|
|
|
}
|
|
|
if (R > mid) {
|
|
|
add(L, R, C, mid + 1, r, rt << 1 | 1);
|
|
|
}
|
|
|
pushUp(rt);
|
|
|
}
|
|
|
|
|
|
// 1~6 累加和是多少? 1~8 rt
|
|
|
public long query(int L, int R, int l, int r, int rt) {
|
|
|
if (L <= l && r <= R) {
|
|
|
return sum[rt];
|
|
|
}
|
|
|
int mid = (l + r) >> 1;
|
|
|
pushDown(rt, mid - l + 1, r - mid);
|
|
|
long ans = 0;
|
|
|
if (L <= mid) {
|
|
|
ans += query(L, R, l, mid, rt << 1);
|
|
|
}
|
|
|
if (R > mid) {
|
|
|
ans += query(L, R, mid + 1, r, rt << 1 | 1);
|
|
|
}
|
|
|
return ans;
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
public static class Right {
|
|
|
public int[] arr;
|
|
|
|
|
|
public Right(int[] origin) {
|
|
|
arr = new int[origin.length + 1];
|
|
|
for (int i = 0; i < origin.length; i++) {
|
|
|
arr[i + 1] = origin[i];
|
|
|
}
|
|
|
}
|
|
|
|
|
|
public void update(int L, int R, int C) {
|
|
|
for (int i = L; i <= R; i++) {
|
|
|
arr[i] = C;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
public void add(int L, int R, int C) {
|
|
|
for (int i = L; i <= R; i++) {
|
|
|
arr[i] += C;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
public long query(int L, int R) {
|
|
|
long ans = 0;
|
|
|
for (int i = L; i <= R; i++) {
|
|
|
ans += arr[i];
|
|
|
}
|
|
|
return ans;
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
public static int[] genarateRandomArray(int len, int max) {
|
|
|
int size = (int) (Math.random() * len) + 1;
|
|
|
int[] origin = new int[size];
|
|
|
for (int i = 0; i < size; i++) {
|
|
|
origin[i] = (int) (Math.random() * max) - (int) (Math.random() * max);
|
|
|
}
|
|
|
return origin;
|
|
|
}
|
|
|
|
|
|
public static boolean test() {
|
|
|
int len = 100;
|
|
|
int max = 1000;
|
|
|
int testTimes = 5000;
|
|
|
int addOrUpdateTimes = 1000;
|
|
|
int queryTimes = 500;
|
|
|
for (int i = 0; i < testTimes; i++) {
|
|
|
int[] origin = genarateRandomArray(len, max);
|
|
|
SegmentTree seg = new SegmentTree(origin);
|
|
|
int S = 1;
|
|
|
int N = origin.length;
|
|
|
int root = 1;
|
|
|
seg.build(S, N, root);
|
|
|
Right rig = new Right(origin);
|
|
|
for (int j = 0; j < addOrUpdateTimes; j++) {
|
|
|
int num1 = (int) (Math.random() * N) + 1;
|
|
|
int num2 = (int) (Math.random() * N) + 1;
|
|
|
int L = Math.min(num1, num2);
|
|
|
int R = Math.max(num1, num2);
|
|
|
int C = (int) (Math.random() * max) - (int) (Math.random() * max);
|
|
|
if (Math.random() < 0.5) {
|
|
|
seg.add(L, R, C, S, N, root);
|
|
|
rig.add(L, R, C);
|
|
|
} else {
|
|
|
seg.update(L, R, C, S, N, root);
|
|
|
rig.update(L, R, C);
|
|
|
}
|
|
|
}
|
|
|
for (int k = 0; k < queryTimes; k++) {
|
|
|
int num1 = (int) (Math.random() * N) + 1;
|
|
|
int num2 = (int) (Math.random() * N) + 1;
|
|
|
int L = Math.min(num1, num2);
|
|
|
int R = Math.max(num1, num2);
|
|
|
long ans1 = seg.query(L, R, S, N, root);
|
|
|
long ans2 = rig.query(L, R);
|
|
|
if (ans1 != ans2) {
|
|
|
return false;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
public static void main(String[] args) {
|
|
|
int[] origin = { 2, 1, 1, 2, 3, 4, 5 };
|
|
|
SegmentTree seg = new SegmentTree(origin);
|
|
|
int S = 1; // 整个区间的开始位置,规定从1开始,不从0开始 -> 固定
|
|
|
int N = origin.length; // 整个区间的结束位置,规定能到N,不是N-1 -> 固定
|
|
|
int root = 1; // 整棵树的头节点位置,规定是1,不是0 -> 固定
|
|
|
int L = 2; // 操作区间的开始位置 -> 可变
|
|
|
int R = 5; // 操作区间的结束位置 -> 可变
|
|
|
int C = 4; // 要加的数字或者要更新的数字 -> 可变
|
|
|
// 区间生成,必须在[S,N]整个范围上build
|
|
|
seg.build(S, N, root);
|
|
|
// 区间修改,可以改变L、R和C的值,其他值不可改变
|
|
|
seg.add(L, R, C, S, N, root);
|
|
|
// 区间更新,可以改变L、R和C的值,其他值不可改变
|
|
|
seg.update(L, R, C, S, N, root);
|
|
|
// 区间查询,可以改变L和R的值,其他值不可改变
|
|
|
long sum = seg.query(L, R, S, N, root);
|
|
|
System.out.println(sum);
|
|
|
|
|
|
System.out.println("对数器测试开始...");
|
|
|
System.out.println("测试结果 : " + (test() ? "通过" : "未通过"));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|