线段树--线段树【模板1】P3372

题目描述

如题,已知一个数列,你需要进行下面两种操作:

1.将某区间每一个数加上x

2.求出某区间每一个数的和

输入格式

第一行包含两个整数N、M,分别表示该数列数字的个数和操作的总个数。

第二行包含N个用空格分隔的整数,其中第i个数字表示数列第i项的初始值。

接下来M行每行包含3或4个整数,表示一个操作,具体如下:

操作1: 格式:1 x y k 含义:将区间[x,y]内每个数加上k

操作2: 格式:2 x y 含义:输出区间[x,y]内每个数的和

输出格式

输出包含若干行整数,即为所有操作2的结果。

 

对于线段树中的每一个非叶子节点[a,b],它的左儿子表示的区间为[a,(a+b)/2],右儿子表示的区间为[(a+b)/2+1,b]。

我们就可以得出节点i的权值=她的左儿子权值+她的右儿子权值。一颗二叉树,她的左儿子和右儿子编号分别是她*2和她*2+1

1.构建树

 1 inline void add(int x, int l, int r, ll y)
 2 {
 3     c[x] += (r - l + 1) * y;
 4     lazy[x] += y;
 5 }
 6 inline void pushup(int x)
 7 {
 8     c[x] = c[x*2] + c[x*2+1];
 9 }
10 inline void pushdown(int x, int l, int r)
11 {
12     add(x*2, l, (l+r)/2, lazy[x]);
13     add(x*2+1, (l+r)/2 + 1, r, lazy[x]);
14     lazy[x] = 0;
15 }
16 inline void build(int x, int l, int r)
17 {
18     if(l == r)
19     return c[x] = a[l], void();
20     build(x*2, l, (l+r)/2);
21     build(x*2+1, (l+r)/2 + 1, r);
22     pushup(x);
23 }

2.单点修改

 1 inline void revise(int x, int l, int r, int l1, int r1, ll y){
 2     if(l1 <= l && r <= r1)
 3         return add(x, l, r, y);
 4     if(lazy[x])
 5         pushdown(x, l, r);
 6     if(l1 <= (l+r)/2)
 7         revise(x*2, l, (l+r)/2, l1, r1, y);
 8     if(r1 > (l+r)/2)
 9         revise(x*2+1, (l+r)/2 + 1, r, l1, r1, y);
10     pushup(x);
11 }

3.区间查询

1、如果这个区间被完全包括在目标区间里面,直接返回这个区间的值

2、如果这个区间的左儿子和目标区间有交集,那么搜索左儿子

3、如果这个区间的右儿子和目标区间有交集,那么搜索右儿子

 1 inline ll query(int x, int l, int r, int l1, int r1){
 2     if(l1 <= l && r <= r1)
 3         return c[x];
 4     if(lazy[x])
 5         pushdown(x, l, r);
 6     ll sum = 0;
 7     if(l1 <= (l+r)/2)
 8         sum += query(x*2, l, (l+r)/2, l1, r1);
 9     if(r1 > (l+r)/2)
10         sum += query(x*2+1, (l+r)/2 + 1, r, l1, r1);
11     return sum;
12 }

这道题的完整代码:

 1 #include <algorithm>
 2 #include <cmath>
 3 #include <cstdio>
 4 #include <iostream>
 5 #include <cstring>
 6 #define ll long long
 7 using namespace std;
 8 const int N = 500000;
 9 int n, m, l, r, ans;
10 ll k, a[N], c[N], lazy[N];
11 inline void add(int x, int l, int r, ll y)
12 {
13     c[x] += (r - l + 1) * y;
14     lazy[x] += y;
15 }
16 inline void pushup(int x)
17 {
18     c[x] = c[x*2] + c[x*2+1];
19 }
20 inline void pushdown(int x, int l, int r)
21 {
22     add(x*2, l, (l+r)/2, lazy[x]);
23     add(x*2+1, (l+r)/2 + 1, r, lazy[x]);
24     lazy[x] = 0;
25 }
26 inline void build(int x, int l, int r)
27 {
28     if(l == r)
29     return c[x] = a[l], void();
30     build(x*2, l, (l+r)/2);
31     build(x*2+1, (l+r)/2 + 1, r);
32     pushup(x);
33 }
34 inline ll query(int x, int l, int r, int l1, int r1){
35     if(l1 <= l && r <= r1)
36         return c[x];
37     if(lazy[x])
38         pushdown(x, l, r);
39     ll sum = 0;
40     if(l1 <= (l+r)/2)
41         sum += query(x*2, l, (l+r)/2, l1, r1);
42     if(r1 > (l+r)/2)
43         sum += query(x*2+1, (l+r)/2 + 1, r, l1, r1);
44     return sum;
45 }
46 inline void revise(int x, int l, int r, int l1, int r1, ll y){
47     if(l1 <= l && r <= r1)
48         return add(x, l, r, y);
49     if(lazy[x])
50         pushdown(x, l, r);
51     if(l1 <= (l+r)/2)
52         revise(x*2, l, (l+r)/2, l1, r1, y);
53     if(r1 > (l+r)/2)
54         revise(x*2+1, (l+r)/2 + 1, r, l1, r1, y);
55     pushup(x);
56 }
57
58 int main(){
59     scanf("%d %d", &n, &m);
60     for (int i = 1;i <= n;i++)
61         scanf("%lld", &a[i]);
62     build(1, 1, n);
63     for (int i = 1;i <= m;i++){
64         scanf("%d %d %d", &ans, &l, &r);
65         if(ans & 1)
66             scanf("%lld", &k), revise(1, 1, n, l, r, k);
67         else
68             printf("%lld\n", query(1, 1, n, l, r));
69     }
70     return 0;
71 }

最后,还要感谢yxl的耐心指导,whx还需要再内化一段时间的线段树哇

原文地址:https://www.cnblogs.com/very-beginning/p/12242568.html

时间: 2024-10-29 06:04:02

线段树--线段树【模板1】P3372的相关文章

线段树 + 区间更新 + 模板 ---- poj 3468

A Simple Problem with Integers Time Limit: 5000MS   Memory Limit: 131072K Total Submissions: 59798   Accepted: 18237 Case Time Limit: 2000MS Description You have N integers, A1, A2, ... , AN. You need to deal with two kinds of operations. One type of

hdu1698(线段树区间替换模板)

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1698 题意: 第一行输入 t 表 t 组测试数据, 对于每组测试数据, 第一行输入一个 n , 表示钩子有 n 节, 编号为 1 ~ n, 每节钩子的初始价值为 1 , 接下来输入一个 q, 接着 q 行输入, 每行格式为 l, r, x, 表示讲区间 [l, r] 内的钩子价值变成 x , 求最终的总价值: 思路: 线段树区间替换模板 代码: 1 #include <iostream> 2 #

HDU 1166 敌兵布阵 (我的树状数组加线段树点修改模板)

思路:本题因为是点修改,所以我们可以用线段树或者是树状数组了.线段树的基本操作我在我的代码中会具体体现,关键是要理解下面这幅图,具体的思想大家可以去看看其他的资料 线段树AC代码: #include<iostream> #include<cstdio> #include<cmath> #include<cstring> #include<algorithm> using namespace std; #define N 50005 int num

#树# #线段树#

线段树 线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点. 线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点. 对于线段树中的每一个非叶子节点[a,b],它的左儿子表示的区间为[a,(a+b)/2],右儿子表示的区间为[(a+b)/2+1,b].因此线段树是平衡二叉树,最后的子节点数目为N,即整个线段区间的长度. 模板: 建树 1 void Pushup(int rt){//根节点

知识点 - 线段树 权值 树套树 二维 可持续

知识点 - 线段树 权值 树套树 二维 可持续 //区间更新求和 inline int ls(int p) { return p << 1; }//左儿子 inline int rs(int p) { return p << 1 | 1; }//右儿子 void push_up(int p) { t[p] = t[ls(p)] + t[rs(p)]; }// 向上不断维护区间操作 void build(ll p, ll l, ll r) { if (l == r) { t[p] =

[qbzt寒假]线段树和树状数组

树状数组 \(lowbit(x)=x\&(-x)\) 二维树状数组 修改某个点,查询(1,1)到(n,m)的前缀和(树状数组要从1开始) HDU2642 Stars \(YFF\)是个浪漫的人,他喜欢数天上的星星. 为了解决这个问题,我们考虑到天空是一个二维平面,有时星星会很亮,有时星星会很暗.首先,天空中没有明亮的星星,然后一些信息会被给出为"\(B\) \(x\) \(y\)",其中"\(B\)"表示明亮,\(x\)表示\(x\)坐标,\(y\)表示在\

BZOJ_3196_二逼平衡树(树套树:线段树+Treap)

描述 可以处理区间问题的平衡树. 分析 树套树.可以用线段树套Treap.人生第一道树套树的题... op1:如果在整区间,直接在该区间的treap上求解.否则分两个区间求解,然后相加.最后+1. op2:这个不太好直接做,可以二分,每次假定一个值,用这个值去做op1,以此求得一个rank=k+1的数,求rank=k的数等价与求这个数的前驱pre. op3:先删后加. op4&op5:如果在整区间,直接在该区间的treap上求解,否则分量个区间求解,pre取最大值,suc取最小值.注意有些数在有

BZOJ3295 动态逆序对 树套树, 树状数组套线段树(主席树)

Orz黄学长,蒟蒻在黄学长的带领下,通过阅读黄学长的代码!终于会了这道题! 首先我想先说一下这道题的思路(准确来说是黄学长的). 很明显,树状数组应该不用讲吧!关键是内存怎么开,维护一些什么样的数据? 其实我们通过观察,很快可以发现,你维护被删的数比维护所有的数轻松多了(不管是空间上,还是时间上).所以我们就可以从这方面想!(其实我一开始的思路,因为这道题我已经看过很久了,一直想写,毕竟是白书里面的一道例题嘛!一开始,蒟蒻的我是打算这样的用树状数组套权值线段树,并且是维护所有的数,我发现空间不够

归并树 划分树 可持久化线段树(主席树) 入门题 hdu 2665

如果题目给出1e5的数据范围,,以前只会用n*log(n)的方法去想 今天学了一下两三种n*n*log(n)的数据结构 他们就是大名鼎鼎的 归并树 划分树 主席树,,,, 首先来说两个问题,,区间第k大 ,,,, 这个问题的通用算法是 划分树,, 说白一点就是把快速排序的中间结果存起来, 举个栗子 原数列 4 1 8 2 6 9 5 3 7 sorted 1 2 3 4 5 6 7 8 9 ........................... qs[0] 4 1 8 2 6 9 5 3 7 q

北大ACM暑期培训(1)——线段树,树状数组

本文出自:http://blog.csdn.net/svitter 今天ACM暑期实训开始了,今天讲述的内容是: 7.14  数据结构(一): 线段树,树状数组,二维线段树. 线段树:invertal tree (称为区间树更加合适) 作用:快速区间查询,用于解决区间统计的有关问题. 重点:同层节点不重叠. 每层最多有两个终止节点. 更新和进行区间分解的时间复杂度均为log(n); 方法:调用会多次使用递归更新插入查询: 空间:开空间的时候,一般情况下开4n大小,2*2log[n] - 1 <=