import java.io.*;
class Node {
int l; // 区间左端点
int r; // 区间右端点
long sum; // 区间和
long lazy; // 懒惰标记
public Node(int l, int r, long sum, long lazy) {
this.l = l;
this.r = r;
this.sum = sum;
this.lazy = lazy;
}
}
/**
* @author wangshaoyu
*/
public class P3372 {
static int n; // 数据范围 [1, n]
static int[] array; // 原始数组
static Node[] tree; // 线段树
// 线段数树初始化
static void init() {
tree = new Node[n * 4];
for (int i = 1; i < tree.length; i++) {
tree[i] = new Node(0, 0, 0, 0);
}
}
// 建树,用回溯从下往上建
static void build(int l, int r, int num) {
tree[num].l = l;
tree[num].r = r;
// 如果区间长度为 1,递归结点
if (l == r) {
tree[num].sum = array[l];
return;
}
int mid = (l + r) / 2;
build(l, mid, num * 2);
build(mid + 1, r, num * 2 + 1);
update(num); // 记得更新结点的值
}
// 更新结点的值
static void update(int num) {
tree[num].sum = tree[num * 2].sum + tree[num * 2 + 1].sum;
}
// 区间修改,区间类型有三种情况
// 将区间[l, r]每个都加上 value
static void modifySegment(int l, int r, int num, int value) {
// 如果找到要修改的区间
if (tree[num].l == l && tree[num].r == r) {
// 这个结点的值确实是这个结点的值,但是它的子结点的值是还没修改的,因此查询的时候需要先修改子结点的值
tree[num].lazy += value;
tree[num].sum += (r - l + 1) * value; // 添加懒惰标记,暂时不需要给儿子结点修改值
return;
}
// 注意:修改区间也要 pushDown
if (tree[num].lazy != 0) {
pushDown(num);
}
int mid = (tree[num].l + tree[num].r) / 2;
// 修改的区间都包含在左子树里
if (r <= mid) {
modifySegment(l, r, num * 2, value);
}
// 修改的区间都包含在右子树里
else if (l > mid) {
modifySegment(l, r, num * 2 + 1, value);
}
// 将要修改的区间拆开
else {
modifySegment(l, mid, num * 2, value);
modifySegment(mid + 1, r, num * 2 + 1, value);
}
update(num);
}
// 用于将懒惰标记下传给儿子们,然后更新儿子们的值,清空当前结点的懒惰标记
// 当前结点的懒惰标记是对儿子结点的值起作用的,对自己是无关的
static void pushDown(int num) {
// 如果是叶子结点
if (tree[num].l == tree[num].r) {
tree[num].lazy = 0;
return;
}
tree[num * 2].lazy += tree[num].lazy;
tree[num * 2 + 1].lazy += tree[num].lazy;
tree[num * 2].sum += (tree[num * 2].r - tree[num * 2].l + 1) * tree[num].lazy;
tree[num * 2 + 1].sum += (tree[num * 2 + 1].r - tree[num * 2 + 1].l + 1) * tree[num].lazy;
tree[num].lazy = 0;
}
// 区间查询
static long query(int l, int r, int num) {
if (tree[num].lazy != 0) {
pushDown(num);
}
if (tree[num].l == l && tree[num].r == r) {
return tree[num].sum;
}
int mid = (tree[num].l + tree[num].r) / 2;
if (r <= mid) {
return query(l, r, num * 2);
}
if (l > mid) {
return query(l, r, num * 2 + 1);
}
return query(l, mid, num * 2) + query(mid + 1, r, num * 2 + 1);
}
public static void main(String[] args) throws IOException {
n = nextInt();
int m = nextInt(); // 总操作个数
array = new int[n + 1];
for (int i = 1; i <= n; i++) {
array[i] = nextInt();
}
// 开始建树
init();
build(1, n, 1);
// 开始查询
for (int i = 0; i < m; i++) {
int oper = nextInt();
int x = nextInt();
int y = nextInt();
if (oper == 1) {
int k = nextInt();
modifySegment(x, y, 1, k);
// 测试数据
// System.out.println("******************");
// for (int j = 1; j <= 7; j++) {
// System.out.println(tree[j].l + " " + tree[j].r + " " + tree[j].sum + " " + tree[j].lazy);
// }
// System.out.println("*******************");
}
else {
long ans = query(x, y, 1);
pw.println(ans);
}
}
pw.flush();
}
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static StreamTokenizer st = new StreamTokenizer(br);
static PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out));
static int nextInt() throws IOException {
st.nextToken();
return (int) st.nval;
}
}
洛谷P3372 线段树模板1 Java
最新推荐文章于 2024-10-29 22:07:14 发布