线段树poj3468

Description

You have N integers, A1, A2, ... , AN. You need to deal with two kinds of operations. One type of operation is to add some given number to each number in a given interval. The other is to ask for the sum of numbers in a given interval.

Input

The first line contains two numbers N and Q. 1 ≤ N,Q ≤ 100000.
The second line contains N numbers, the initial values of A1, A2, ... , AN. -1000000000 ≤ Ai ≤ 1000000000.
Each of the next Q lines represents an operation.
"C a b c" means adding c to each of AaAa+1, ... , Ab. -10000 ≤ c ≤ 10000.
"Q a b" means querying the sum of AaAa+1, ... , Ab.

Output

You need to answer all Q commands in order. One answer in a line.

Sample Input

10 5
1 2 3 4 5 6 7 8 9 10
Q 4 4
Q 1 10
Q 2 4
C 3 6 3
Q 2 4

Sample Output

4
55
9
15

tip:以前没有注意到,在void函数中return的意思是强行终止这次活动,继续往下走

问题:接收输入 、建树过程、这儿涉及到线段树一个区间更新的问题

下面一段话摘自:https://www.cnblogs.com/yakoazz/p/5877187.html

以上就是线段树最简单的功能——单点更新。

        下面为大家带来的线段树稍微难一点但是基本是最常用的一个用法:区间更新。

区间更新对于初学者来说是一个坎,其中有几步相对较难理解。但是只要掌握,就能解决绝大多数线段树的题目了。

        首先刚刚那个题每次是每个营地增减人,那么如果每次是x号营地到y号营地每次都增减人呢?这样我们就会发现单点更新操作不适用了,无论我们如何调整都无法达到效果,而且即使每次对于x到y之间每个营地都执行一次单点操作,结果上看似可以,但是极限情况下我们每次对于1到n号进行更新的话,复杂度就会达到O(m*n*logn),这样就绝对会超时了,那么怎么做呢?这里就要用到区间更新了。

     

 但是与单点更新最大的不同就是:它多了一个lazy数组!!!!!!!!!!重要的地方要打10个感叹号。

        laz,全称lazy,中文叫懒惰标记或者延迟更新标记。

        因为我们知道,如果我们每次都把段更新到节点上,那么操作次数和每次对区间里面的每个点单点更新是完全一样的哇!那么怎么办呢?仔细观察线段树,你会发现一个非常神奇的地方:每个节点表示的值都是区间[le,ri]之间的值有木有!!!!!!!!!!为什么说它神奇呢?更新的区间的值,存的区间的值!简直就是天作之合,我每次更新到对应区间了我就放着,我等下次需要往下更新更小的区间的时候,再把两次的值一起更新下去有木有啊!可以节约非常多时间啊有木有啊!

       对,这就是laz[num]的作用。下面我们跟着题再来逐步感受。

       首先在最最最最最最开始,是没有进行过更新操作的,那么laz[num]自然是全部置为0(当然有的题有额外的初始化要求,大家根据题目自行定夺)。

那篇博客了里有具体的板子代码,,好好看一看哟,

这个题代码,,思路没有错,但是一直会报数组越界,,,下次在看到会把它改过来

import java.util.Scanner;

public class Main {
    static int[] t;
    static Node[] node;
    static long SUM;

    public static void main(String[] args) {
        Scanner cin = new Scanner(System.in);
        int n = cin.nextInt();
        int m = cin.nextInt();
        t = new int[n+1];
        node = new Node[n << 2]; //如果区间长度为n的花,就要给开四倍的空间大小的数组
        make(0, n, 0);
        for (int i = 1; i <= n; i++) {     //必须要从1 开始??
            t[i] = cin.nextInt();
        }
        for (int i = 0; i < m; i++) {
            String op = cin.next(); //有四次操作
            int l = cin.nextInt(); //要查询的化要输入左右端点
            int r = cin.nextInt();
            if ("Q".equals(op)) {
                SUM = 0;

                query(l, r, 0);
                System.out.println(SUM);
            } else { //给某个区间加上一个数字
                int val = cin.nextInt();
                update(l, r, 0, val); //从根结点开始value的

            }
        }
    }

    static void make(int l, int r, int idx) {
        node[idx] = new Node(l, r);//这是最顶的节点
        if (l == r) {
            node[idx].sum = t[r];  //到叶子节点的的化 节点的值就只有一个,他自己就是他自己

        } else {
            int mid = (l + r) >> 1;

            make(l, mid, (idx << 1) | 1);//|是加一的意思
            make(mid + 1, l, (idx >> 1) + 2);
            //然后左右子树建立好之后要更新父节点
            pushUp(idx);
        }
    }

    static void pushUp(int idx) {
        node[idx].sum = node[(idx << 1) | 1].sum + node[(idx << 1) + 2].sum;

    }

    static void query(int l, int r, int idx) {
        if (l <= node[idx].l && r >= node[idx].r) {
            SUM += node[idx].sum;
        } else {
            //lazy down
            if (node[idx].tag != 0)//加上lazy标签后可能就会有的地方已经变了,但是还没有更改,所以需要下放一下
                pushDown(idx);
            int mid = (node[idx].l + node[idx].r) >> 1;
            if (r <= mid) {
                query(l, r, (idx << 1) | 1);   //其实这里我不太懂为什么l和r不变
            } if (l > mid) {
                query(l, r, (idx << 1) + 2);
            }
        }

    }

    static void update(int l, int r, int idx, int val) { //这里区间更新,如果从上往下会乱掉,从下往上又会分割成每一小格会退化,所以要用lazy
        if (l <= node[idx].l && r >= node[idx].r) {
            node[idx].sum += (node[idx].r - node[idx].l + 1) * val;
            node[idx].tag += val;
        } else {
            if (node[idx].tag != 0) {   //更新左右区间时先看看当前节点的tag有没有值 有的话就县下放 放完在更新左右区间
                pushDown(idx);
                int mid = (node[idx].l + node[idx].r) >> 1;
                if (r <= mid) {
                    update(l, r, (idx << 1) | 1, val);
                }
                if (l > mid) {
                    update(l, r, (idx << 1) + 2, val);
                }
                pushUp(idx); // 把最后的子节点更新的值往回传
            }

        }

    }

    static void pushDown(int idx) {
        long val = node[idx].tag; //获取val值
        int mid = (node[idx].l + node[idx].r) >> 1;
        node[(idx << 1) | 1].sum += (mid - node[idx].l + 1) * val;
        node[(idx << 1) + 2].sum += (node[idx].r - mid + 1) * val;
        node[(idx << 1) | 1].tag += val;
        node[(idx << 1) + 2].tag += val;  //左右孩子的tag都变
        node[idx].tag = 0; //当前节点下放后就把tag改回来
    }
    static class Node{
        int l,r;
        long sum,tag; //tag是lazy标签

        Node(int l,int r){
            this.l = l;
            this.r = r;
        }
    }

}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值