ALGO-8 操作格子

ALGO-8 操作格子

题目

资源限制

时间限制:1.0s 内存限制:256.0MB

问题描述

有 n 个格子,从左到右放成一排,编号为 1-n。
共有 m 次操作,有 3 种操作类型:

  1. 修改一个格子的权值,
  2. 求连续一段格子权值和,
  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;
    }

}
posted @ 2022-03-18 13:32  morning-start  阅读(31)  评论(0编辑  收藏  举报