线段树基础

关于线段树的原理学习,可以参看杨弋大牛的论文《线段树》以及刘汝佳老师的《算法竞赛入门经典(训练指南)》,代码风格学习hzwer或者notonlysuccess均可。

一.单点更新

最基础的线段树

题目:codevs1080

链接:http://codevs.cn/problem/1080/

分析:最简单的线段树,单点更新,区间求和

 1 #include<iostream>
 2 #include<cstdio>
 3 using namespace std;
 4 const int maxn=100000+10;
 5 struct data{
 6     int left,right; //左右端点
 7     int sum; //结点的和
 8 }Tree[maxn<<2];
 9 int n,m;
10 int a[maxn];
11 //建树过程
12 void build(int l,int r,int rt){
13     Tree[rt].left=l;Tree[rt].right=r;
14     if(l==r){  //叶结点直接插入值
15         Tree[rt].sum=a[l];
16         return;
17     }
18     int mid=(l+r)>>1;
19     build(l,mid,rt<<1);
20     build(mid+1,r,rt<<1|1);
21     Tree[rt].sum=Tree[rt<<1].sum+Tree[rt<<1|1].sum;
22 }
23
24 //单点更新
25 void update(int x,int y,int rt){
26     Tree[rt].sum+=y;
27     int l=Tree[rt].left,r=Tree[rt].right;
28     if(l==r) return;
29     int mid=(l+r)>>1;
30     if(x<=mid)  update(x,y,rt<<1);
31     else update(x,y,rt<<1|1);
32 }
33
34 //区间求和
35 int query(int l,int r,int rt){
36     int L=Tree[rt].left,R=Tree[rt].right;
37     if(L==l&&r==R){
38         return Tree[rt].sum;
39     }
40     int mid=(L+R)/2;
41     if(r<=mid) return query(l,r,rt<<1);
42     if(l>mid) return query(l,r,rt<<1|1);
43     return query(l,mid,rt<<1)+query(mid+1,r,rt<<1|1);
44 }
45 int main()
46 {
47     scanf("%d",&n);
48     for(int i=1;i<=n;i++){
49         scanf("%d",&a[i]);
50     }
51     build(1,n,1);
52     scanf("%d",&m);
53     while(m--){
54         int num,x,y;
55         scanf("%d%d%d",&num,&x,&y);
56         if(num==1) update(x,y,1);
57         else printf("%d\n",query(x,y,1));
58     }
59     return 0;
60 }

二.区间更新

从单点更新一直到区间更新是初学者的一道坎,主要我们要深刻理解lazy操作,因为区间更新有时候并不需要像单点那样更新到子节点

推荐一篇博客:http://blog.csdn.net/dreamzuora/article/details/52781831

题目:codevs1082

链接:http://codevs.cn/problem/1082/

分析:区间更新,区间求和

 1 #include "iostream"
 2 #include "cstdio"
 3 #include "cstring"
 4 using namespace std;
 5 const int maxn=200000+10;
 6 struct data{
 7     int left,right;
 8     long long sum; //区间和
 9     int val;  //标记延迟
10 }Tree[maxn<<2];
11 int a[maxn];
12 int n,m;
13
14 //建树
15 void build(int l,int r,int rt){
16     Tree[rt].left=l;Tree[rt].right=r;
17     if(l==r){
18         Tree[rt].sum=a[l];
19         return;
20     }
21     int mid=(l+r)>>1;
22     Tree[rt].val=0;
23     build(l,mid,rt<<1);
24     build(mid+1,r,rt<<1|1);
25     Tree[rt].sum=Tree[rt<<1].sum+Tree[rt<<1|1].sum;
26 }
27
28 //更新子节点
29 void pushdown(int rt){
30     int x=Tree[rt].right-Tree[rt].left+1;
31     Tree[rt<<1].val+=Tree[rt].val;
32     Tree[rt<<1|1].val+=Tree[rt].val;
33     Tree[rt<<1].sum+=(x-(x>>1))*Tree[rt].val;
34     Tree[rt<<1|1].sum+=(x>>1)*Tree[rt].val;
35     Tree[rt].val=0;
36 }
37
38 //区间更新
39 void update(int l,int r,int x,int rt){
40     int L=Tree[rt].left,R=Tree[rt].right;
41     if(L==l&&R==r){
42         Tree[rt].val+=x;
43         Tree[rt].sum+=(R-L+1)*x;
44         return;
45     }
46     int mid=(L+R)>>1;
47     if(Tree[rt].val) pushdown(rt);
48     if(r<=mid) update(l,r,x,rt<<1);
49     else if(l>mid)  update(l,r,x,rt<<1|1);
50     else{
51         update(l,mid,x,rt<<1);
52         update(mid+1,r,x,rt<<1|1);
53     }
54     Tree[rt].sum=Tree[rt<<1].sum+Tree[rt<<1|1].sum;
55 }
56
57 //区间查询
58 long long query(int l,int r,int rt){
59     int L=Tree[rt].left,R=Tree[rt].right;
60     if(L==l&&R==r){
61         return Tree[rt].sum;
62     }
63     if(Tree[rt].val) pushdown(rt);
64     int mid=(L+R)>>1;
65     if(r<=mid) return query(l,r,rt<<1);
66     if(l>mid) return query(l,r,rt<<1|1);
67     return query(l,mid,rt<<1)+query(mid+1,r,rt<<1|1);
68 }
69 int main()
70 {
71     scanf("%d",&n);
72     for(int i=1;i<=n;i++)
73         scanf("%d",&a[i]);
74     build(1,n,1);
75     scanf("%d",&m);
76     while(m--){
77         int num;
78         int x,y,d;
79         scanf("%d",&num);
80         if(num==1){
81             scanf("%d%d%d",&x,&y,&d);
82             update(x,y,d,1);
83         }else{
84             scanf("%d%d",&x,&y);
85             printf("%lld\n",query(x,y,1));
86         }
87     }
88 }

时间: 2024-12-07 18:53:18

线段树基础的相关文章

线段树 基础单点更新 敌兵布阵

题:敌兵布阵 标准线段树模板代码: #include<cstdio> #include<cstring> const int maxn = 500000 + 10; struct Node{ int left, right, count; }node[maxn]; int a[maxn]; /*********************************** ***************建树**************** ************i是区间序号********

线段树基础知识----(基础数据结构)--(一)

1.定义 引入:为什么要使用线段树而不用数组模拟呢? answer:因为有些题用数组来做就要超时,用线段树的O(log(n))的时间复杂度刚好可以求解 毫无疑问线段树是一种数据结构,但是它实际是一个类似树状的链表结构(个人认为) ///还是要正经一点(照搬教科书)----------- / ////////////////////////////////////////////////////////////////////// 线段树定义:线段树是一种二叉搜索树,与区间树相似,它将一个区间划分

最大最小值(线段树基础版)

最大最小值 时间限制:1000 ms  |  内存限制:65535 KB 描述 给出N个整数,执行M次询问. 对于每次询问,首先输入三个整数C.L.R: 如果C等于1,输出第L个数到第R个数之间的最小值: 如果C等于2,输出第L个数到第R个数之间的最大值: 如果C等于3,输出第L个数到第R个数之间的最小值与最大值的和. (包括第L个数和第R个数). 输入 首先输入一个整数T(T≤100),表示有T组数据. 对于每组数据,先输入一个整数N(1≤N≤10000),表示有N个整数: 接下来一行有N个整

HDU 1754--I Hate It(线段树基础)

I Hate It Time Limit: 9000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 44820    Accepted Submission(s): 17591 Problem Description 很多学校流行一种比较的习惯.老师们很喜欢询问,从某某到某某当中,分数最高的是多少. 这让很多学生很反感.不管你喜不喜欢,现在需要你做的是,就是按照老师的

[JSOI2008]最大数(线段树基础)

题目描述 现在请求你维护一个数列,要求提供以下两种操作: 1. 查询操作. 语法:Q L 功能:查询当前数列中末尾L个数中的最大的数,并输出这个数的值. 限制: L 不超过当前数列的长度.(L > 0) 2. 插入操作. 语法:A n 功能:将 n 加上 t ,其中 t 是最近一次查询操作的答案(如果还未执行过查询操作,则 t=0 ),并将所得结果对一个固定的常数 D  取模,将所得答案插入到数列的末尾. 限制: n 是整数(可能为负数)并且在长整范围内. 注意:初始时数列是空的,没有一个数.

【线段树基础】NKOJ 1321 数列操作

时间限制 : 10000 MS   空间限制 : 165536 KB 问题描述 假设有一列数{Ai}(1≤i≤n),支持如下两种操作:将Ak的值加D.(k, D是输入的数)输出As+As+1+…+At.(s, t都是输入的数,S≤T) 输入格式 第一行一个整数n,第二行为n个整数,表示{Ai}的初始值≤10000.第三行为一个整数m,表示操作数下接m行,每行描述一个操作,有如下两种情况:ADD k d (表示将Ak加d,1<=k<=n,d为数,d的绝对值不超过10000)SUM s t (表示

POJ2528线段树基础

开始就直接用延迟标记搞了下,最后发现内存肯定会爆了,数据太大了: 问了瓜神,原来应该用离散化来做这题,具体见注释 #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <climits> #include <string> #include <iostream> #include <map> #in

I Hate It(线段树基础)

Time Limit: 9000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 45287    Accepted Submission(s): 17773 Problem Description 很多学校流行一种比较的习惯.老师们很喜欢询问,从某某到某某当中,分数最高的是多少.这让很多学生很反感. 不管你喜不喜欢,现在需要你做的是,就是按照老师的要求,写一个程序,模拟

Balanced Lineup---poj3264线段树基础

题目链接 求对应区间最大值与最小值的差: #include<stdio.h> #include<string.h> #include<algorithm> #include<iostream> #define INF 0xfffffff #define N 50010 using namespace std; #define Lson r<<1 #define Rson r<<1|1 struct SegTree { int L, R