ALGO-8 操作格子
ALGO-8 操作格子
题目
资源限制
时间限制:1.0s 内存限制:256.0MB
问题描述
有 n 个格子,从左到右放成一排,编号为 1-n。
共有 m 次操作,有 3 种操作类型:
- 修改一个格子的权值,
- 求连续一段格子权值和,
- 求连续一段格子的最大值。
对于每个 2、3 操作输出你所求出的结果。
输入格式
第一行 2 个整数 n,m。
接下来一行 n 个整数表示 n 个格子的初始权值。
接下来 m 行,每行 3 个整数 p,x,y,p 表示操作类型,p=1 时表示修改格子 x 的权值为 y,p=2 时表示求区间[x,y]内格子权值和,p=3 时表示求区间[x,y]内格子最大的权值。
输出格式
有若干行,行数等于 p=2 或 3 的操作总数。
每行 1 个整数,对应了每个 p=2 或 3 操作的结果。
样例输入
4 3
1 2 3 4
2 1 3
1 4 3
3 1 4
样例输出
6
3
数据规模与约定
对于 20%的数据 n <= 100,m <= 200。
对于 50%的数据 n <= 5000,m <= 5000。
对于 100%的数据 1 <= n <= 100000,m <= 100000,0 <= 格子权值 <= 10000。
题解
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.InputStream;
import java.util.StringTokenizer;
public class ALGO_8 {
private static int[] array;
private static Node[] tree;
private static int x, y, sum, max;
//建树
//可以注意第一个节点编号是1,代表区间是[1,n]
public static void buildTree(int k, int left, int right) {
Node node = new Node();
node.setLeft(left);
node.setRight(right);
tree[k] = node;
//如果左右区间相等那就是到了最底层了,也就是所谓的叶子节点
if (left == right) {
//叶子结点的和 以及最大值都是其自己的值,因为只有一个值呀
node.setSum(array[left - 1]);
node.setMax(array[left - 1]);
return;
}
//不是叶子节点就继续往下建
buildTree(k * 2, left, (left + right) / 2);
buildTree(k * 2 + 1, (left + right) / 2 + 1, right);
//结束递归就代表已经把其下的孩子节点都建好了,这时再把sum和max补上
node.setSum(tree[k * 2].getSum() + tree[k * 2 + 1].getSum());
node.setMax(Math.max(tree[k * 2].getMax(), tree[k * 2 + 1].getMax()));
}
//单点修改
public static void update(int k) {
int left = tree[k].getLeft();
int right = tree[k].getRight();
//如果左右区间相等就到了想要修改的点了 毕竟是单点修改 这就到了修改的地方了
if (left == right) {
int n = tree[k].getSum();
tree[k].setSum(y);
tree[k].setMax(y);
//这个是为了往上加的时候省点效率
y = y - n;
return;
}
//看要修改的点是偏向左还是右 二分查找
if (x <= (left + right) / 2) {
update(k * 2);
} else {
update(k * 2 + 1);
}
//把y这个差值补上
tree[k].setSum(tree[k].getSum() + y);
//max也要重新判断了
tree[k].setMax(Math.max(tree[k * 2].getMax(), tree[k * 2 + 1].getMax()));
}
//求最大值跟求和其实差不多 不多说了
public static void max(int k) {
int left = tree[k].getLeft();
int right = tree[k].getRight();
if (x <= left && right <= y) {
max = Math.max(tree[k].getMax(), max);
return;
}
int mid = (left + right) / 2;
if (x <= mid) {
max(k * 2);
}
if (y > mid) {
max(k * 2 + 1);
}
}
//区间求和
public static void sum(int k) {
int left = tree[k].getLeft();
int right = tree[k].getRight();
//如果这个节点所代表的区间在所要查询的区间内,就直接取了
if (x <= left && right <= y) {
sum += tree[k].getSum();
return;
}
//否则继续根据节点区间的中点来判断去左孩子还是右孩子 可能两个都去
int mid = (left + right) / 2;
if (x <= mid) {
sum(k * 2);
}
if (y > mid) {
sum(k * 2 + 1);
}
}
public static void main(String[] args) throws Exception {
//用他替代一下Scanner 就能解决超时问题 当然算法要用对 线段树的
InputReader inputReader = new InputReader(System.in);
int n = inputReader.nextInt();
int m = inputReader.nextInt();
array = new int[n];
for (int i = 0; i < n; i++) {
array[i] = inputReader.nextInt();
}
//4倍是远远能满足的,具体点是n最接近的2的倍数的两倍,比如n是5,最接近是8,两倍就是8 * 2 = 16
//这里直接取5 * 4 = 20 是有空余的
tree = new Node[n * 4];
buildTree(1, 1, n);//构建线段树
while (m-- > 0) {
int k = inputReader.nextInt();
x = inputReader.nextInt();
y = inputReader.nextInt();
switch (k) {
case 1:
update(1);
break;
case 2:
sum(1);
System.out.println(sum);
sum = 0;//清零很重要
break;
case 3:
max(1);
System.out.println(max);
max = 0;//清零很重要
break;
}
}
}
}
//java输入挂 替代Scanner
class InputReader {
private BufferedReader reader;
private StringTokenizer tokenizer;
InputReader(InputStream in) {
reader = new BufferedReader(new InputStreamReader(in));
tokenizer = new StringTokenizer("");
}
private String next() throws IOException {
while (!tokenizer.hasMoreTokens()) {
tokenizer = new StringTokenizer(reader.readLine());
}
return tokenizer.nextToken();
}
public Integer nextInt() throws IOException {
return Integer.parseInt(next());
}
}
//树节点类
class Node {
//节点的左端点
private Integer left;
//节点的右端点
private Integer right;
//节点所代表的区间所有值的和
private Integer sum;
//节点所代表的区间所有值里的最大值
private Integer max;
public Integer getRight() {
return right;
}
public void setRight(Integer right) {
this.right = right;
}
public Integer getLeft() {
return left;
}
public void setLeft(Integer left) {
this.left = left;
}
public Integer getSum() {
return sum;
}
public void setSum(Integer sum) {
this.sum = sum;
}
public Integer getMax() {
return max;
}
public void setMax(Integer max) {
this.max = max;
}
}