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 Aa, Aa+1, ... , Ab. -10000 ≤ c ≤ 10000.
"Q a b" means querying the sum of Aa, Aa+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;
}
}
}