这个题是,我想练习线段树找来做的。所以从一开始就知道它要用线段树来做。参考的是:https://blog.youkuaiyun.com/zearot/article/details/52280189,这个博主的线段树讲解。讲的很好。第一遍看不一定能看懂。多点耐心,慢慢来。最后肯定会理解的。好了,以下是“操作格子”的java代码。我主要是为了给自己记录的,哪里有错误或者问题,非常欢迎指出!因为我很菜的啦。。
package bluebridge2022;
import java.util.ArrayList;
import java.util.Scanner;
public class Operation_grid {
static int maxn = 100007; //元素总个数
static int Sum[] = new int[maxn<<2];//Sum求和,开四倍空间
static int Max[] = new int[maxn<<2];//Sum求和,开四倍空间
static int A[] = new int[maxn];//存原数组下标[1,n]
int n;
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
int m = in.nextInt();
for(int i = 1; i <= n; i++) {
A[i] = in.nextInt();
}
//创建线段树
build(1,n,1);
ArrayList<Integer> arr = new ArrayList<>();//存储答案的容器
int ans = 0;
for (int i = 0; i < m; i++) {
int opera = in.nextInt();
int x = in.nextInt();
int y = in.nextInt();
if(opera == 1) { //修改数值
UpDate(x, y, 1, n, 1);
}else if (opera == 2) { //求区间和
ans = Query(x, y, 1, n, 1);
arr.add(ans);
}else { //求最大值
ans = calMax(x,y,1,n,1);
arr.add(ans);
}
}
in.close();
for(int i = 0; i <arr.size();i++) {
System.out.println(arr.get(i));
}
}
private static int calMax(int L, int R, int l, int r, int rt) {
if(L <= l && r <= R) { //如果现在这个区域在我想要计算的区域内,则返回这个区域的最大值
return Max[rt];
}
int mid = (r+l) / 2;
int temp1 = -1,temp2 = -1;//题目规定权值最小是0
if(L <= mid) {//如果和左边有重叠的地方,得到重叠地方的最大值
temp1 = calMax(L, R, l, mid, rt*2);
}
if(R > mid) {//如果和右边有重叠的地方,得到重叠地方的最大值
temp2 = calMax(L, R, mid+1, r, rt*2+1);
}
return Math.max(temp1, temp2);
}
//[L,R]是我想要计算的区域,[l,r]是当前区域
private static int Query(int L, int R, int l, int r, int rt) {
if(L <= l && r <= R) { //如果现在这个区域在我想要计算的区域内,则返回这个数值
return Sum[rt];
}
int mid = (r+l) / 2;
int ans = 0;
if(L <= mid) {//左边有重叠的部分
ans += Query(L, R, l, mid, rt*2);
}
if(R > mid) {//右边有重叠的部分
ans += Query(L, R, mid+1, r, rt*2+1);
}
return ans;
}
private static void build(int l, int r, int rt) {
if( r == l) { //将叶子结点信息填入
Sum[rt] = A[l];
Max[rt] = A[l];
return;
}
int mid = (r+l) / 2;
build(l, mid, rt*2);//创建左子树
build(mid+1, r, rt*2+1);//创建右子树
PushUp(rt);//更新数据 (当左右子树都创建完了之后才可以计算sum。因为sum的值就是由左右子树得来的)
}
private static void PushUp(int rt) {
Max[rt] = Math.max(Max[rt*2], Max[rt*2+1]);//当前区域的最大值是左右子树区域的最大值的较大者
Sum[rt] = Sum[rt*2] + Sum[rt*2 +1];//当前区域的和是左右子树(子区域)的和
}
//把第i位数据改为k,当前边界为(l,r),当前结点为rt
private static void UpDate(int i, int k, int l, int r, int rt) {
if(l == r) {//到达叶节点,修改叶节点的值
A[i] = k;//改写
Sum[rt] = k;
Max[rt] = k;
return;
}
int mid = (r+l) / 2;
//根据条件判断往左子树调用还是往右
if( i <= mid) {
UpDate(i, k, l, mid, rt*2);
}else {
UpDate(i, k, mid+1, r, rt*2+1);
}
//因为有元素改变了,所以要重新存一遍数据
PushUp(rt);
}
}