线段树简单应用

先附上几张图便与理解,大佬文章传送门1传送门2











  • HDU1166:题目描述

    线段树 +更新单点,求区间总和

代码如下(递归版)

#include<iostream>
#include<string>
using namespace std;
#define MAXN 50005
#define ls l,m,pos << 1
#define rs m+1,r,pos << 1 | 1 int node[MAXN],Sum[MAXN << 2],Add[MAXN << 2]; //上推更新信息、建树
void Push_up(int pos)
{
Sum[pos] = Sum[pos << 1] + Sum[pos << 1 | 1];
} void Build(int l, int r, int pos)
{
if(l == r)
{
Sum[pos] = node[l];
return;
}
//左右递归区间
int m = (l + r) >> 1;
Build(ls);
Build(rs);
//更新信息
Push_up(pos);
} //点的修改
void Update_point(int l, int r, int pos, int x, int c)
{
if(l == r)
{
Sum[pos] += c;
return;
} //看下标x 是在左子区间,还是在有子区间
int m = (l + r) >> 1;
if(x <= m) Update_point(ls, x, c);
else Update_point(rs, x, c);
//回溯的时候从下往上更新 Sum
Push_up(pos);
} //下推做标记、区间的修改
void Push_down(int ln, int rn, int pos)
{
if(Add[pos])
{
//向下标记子区间
Add[pos << 1] += Add[pos];
Add[pos << 1 | 1] += Add[pos];
//更新sum值
Sum[pos << 1] += Add[pos] * ln;
Sum[pos << 1 | 1] += Add[pos] * rn;
//解除当前的标记
Add[pos] = 0;
}
} void Update_area(int l, int r, int pos, int s, int e, int c)
{
if(s <= l && r <= e)
{
Sum[pos] += (r - l + 1) * c;
Add[pos] += c;
return;
}
//对左右区间进行讨论
int m = (l + r) >> 1;
//先下推标记,为更新本节点的 Sum 做准备
Push_down(m - l + 1, r - m, pos);
if(s <= m) Update_area(ls, s, e, c);
if(e > m) Update_area(rs, s, e, c);
//上推更新当前的Sum,因为可能子Sum已经改变
Push_up(pos);
} int Query(int l, int r, int pos, int s, int e)
{
if(s <= l && r <= e)
{
return Sum[pos];
} //左右区间进行讨论,积累答案
int m = (l + r) >> 1;
Push_down(m - l + 1, r - m, pos); //⚠当前的这个下推语句在这个一次查询的时候不会被执行(由于查询区间的限制),但在以后的查询中 起着更新 子Sum的作用(由于以前的某个/些 子区间没有被执行,所以当来到 这个区间的到时候我们要的是 已经更新过的 子区间Sum)
int ans = 0;
if(s <= m) ans += Query(ls, s, e);
if(e > m) ans += Query(rs, s, e);
return ans;
} int main()
{
ios::sync_with_stdio(false); cin.tie(nullptr);
//freopen("T.txt","r",stdin);
string s1 = "Add";
string s2 = "Sub";
string s3 = "Query";
string s4 = "End";
string s;
int t, Case = 1;
cin >> t;
while(t --)
{
cout << "Case "<<Case ++<<":" << endl;
int n;
cin >> n;
for(int i = 1; i <= n; i ++)
cin >> node[i];
Build(1, n, 1);
while(cin >> s && s != s4)
{
int a,b;
cin >> a >> b;
if(s == s1)
Update_point(1, n, 1, a, b);
else if(s == s2)
Update_point(1, n, 1, a,-b);
else
cout << Query(1, n, 1, a, b) << endl;
}
} return 0;
}

代码如下(非递归版)

#include<iostream>
using namespace std;
#define MAXN 50005
#define ls l,m,pos << 1
#define rs m+1,r,pos << 1 | 1
int node[MAXN],Sum[MAXN << 2],Add[MAXN << 2];
int N,n; //建立线段树
void Build(int n)
{
//找n接近的 2 的次方数
N = 1; while(N < n + 2) N <<= 1;
//更新叶节点
for(int i = 1; i <= n; i ++) Sum[i + N] = node[i]; //存储叶节点的下标 + N == 叶节点位于树中的下标 == 存储数据的 node 数组中的下标
//更新非叶节点
for(int i = N - 1; i > 0; i --)
{
//更新所有非叶节点的统计信息
Sum[i] = Sum[i << 1] + Sum[i << 1 | 1];
//清空所有非叶节点的标记
Add[i] = 0;
}
}
//点的修改
void Update_point(int x, int c)
{
for(int pos = N + x; pos; pos >>= 1)
{
Sum[pos] += c;
}
}
//没有标记下的区间查询
int Query(int s, int e)
{
int ans = 0;
for(int L = N + s - 1,R = N + e + 1; L ^ R ^ 1; L >>= 1, R >>= 1)
{
if(~ L & 1) ans += Sum[L ^ 1];
if( R & 1) ans += Sum[R ^ 1];
}
return ans;
} //区间修改
void Update_area(int s, int e, int c)
{
int L,R,Ln = 0,Rn = 0,x = 1;
for(L = N + s -1, R = N + e + 1; L ^ R ^ 1; L >>= 1, R >>= 1, x <<= 1)
{
//更新Sum
Sum[L] += c * Ln;
Sum[R] += c * Rn;
//处理Add标记
if(~ L & 1) Add[L ^ 1] += c,Sum[L ^ 1] += c * x,Ln += x;
if( R & 1) Add[R & 1] += c,Sum[R & 1] += c & x,Rn += x;
}
//更新上层的Sum
for( ; L; L >>= 1, R >>= 1)
{
Sum[L] += c * Ln;
Sum[R] += c * Rn;
}
}
//区间查询
int Query_area(int s, int e)
{
int ans = 0;
int L,R,Ln = 0,Rn = 0,x = 1;
for(L = N + s - 1, R = N + e + 1; L ^ R ^ 1; L >>= 1, R >>= 1, x <<= 1)
{
//如果当前节点有标记(说明该节点的 子节点,子子节点 。。。。 是没有加上标记值的)
ans += Add[L] * Ln;
ans += Add[R] * Rn; //如果该节点是左子节点的左子树 或 右子节点的右子树
if(~ L & 1) ans += Sum[L ^ 1], Ln += x;
if( R & 1) ans += Sum[R ^ 1], Rn += x;
}
//处理上层的标记
for( ; L; L >>= 1, R >>= 1)
{
ans += Add[L] * Ln;
ans += Add[R] * Rn;
}
return ans;
} int main()
{ ios::sync_with_stdio(false); cin.tie(nullptr);
//freopen("T.txt","r",stdin);
string s1 = "Add";
string s2 = "Sub";
string s3 = "Query";
string s4 = "End";
string s;
int t, Case = 1;
cin >> t;
while(t --)
{
cout << "Case "<<Case ++<<":" << endl;
cin >> n;
for(int i = 1; i <= n; i ++)
cin >> node[i];
Build(n);
while(cin >> s && s != s4)
{
int a,b;
cin >> a >> b;
if(s == s1)
Update_point(a, b);
else if(s == s2)
Update_point(a,-b);
else
cout << Query_area(a, b) << endl;
}
} return 0;
}
  • HDU1754: 题目描述

    线段树 +更新单点,求区间最大值!!!

代码如下

#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 200005
#define ls l,m,pos << 1
#define rs m+1,r,pos << 1 | 1 int node[MAXN],Max[MAXN << 2],Add[MAXN << 2]; //向上传递更新信息、建树
void Push_up(int pos)
{
Max[pos] = max(Max[pos << 1], Max[pos << 1 | 1]);
} void Build(int l, int r, int pos)
{
if(l == r)
{
Max[pos] = node[l];
return;
} //分左右子区间进行讨论
int m = (l + r) >> 1;
Build(ls);
Build(rs);
Push_up(pos);
} void Update_point(int l, int r, int pos, int x, int c)
{
if(l == r)
{
Max[pos] = c;
return;
}
//分左右区间进行讨论
int m = (l + r) >> 1;
if(x <= m) Update_point(ls, x, c);
else Update_point(rs, x, c);
Push_up(pos);
} int Query(int l, int r, int pos, int s, int e)
{
if(s <= l && r <= e)
{
return Max[pos];
} //分左右区间
int m = (l + r) >> 1;
int mx = -1;
if(s <= m) mx = max(mx, Query(ls, s, e));
if(e > m) mx = max(mx, Query(rs, s, e));
return mx;
} int main()
{
ios::sync_with_stdio(false); cin.tie(nullptr);
//freopen("T.txt","r",stdin);
int n,m;
while(cin >> n >> m)
{
for(int i = 1; i <= n; i ++)
cin >> node[i];
Build(1, n, 1);
char ch;
int a, b;
while(m --)
{
cin >> ch >> a >> b;
if(ch == 'Q')
cout << Query(1, n, 1, a, b) << endl;
else
Update_point(1, n, 1, a, b);
}
} return 0;
}

代码如下(非递归版)

⚠️ 这个代码 超时了,也没想好怎么优化

#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 100005
int node[MAXN],Max[MAXN << 2];
int n,N; //非递归线段树建树
void Build(int n)
{
//找到一个 > n + 2 的 N
N = 1; while(N < n + 2) N <<= 1;
//处理叶节点的Max问题
for(int i = 1; i <= n; i ++)
Max[N + i] = node[i];
//处理非叶节点的最大值
for(int i = N - 1; i; i --)
Max[i] = max(Max[i << 1], Max[i << 1 | 1]);
} //更新某个节点的值
void Update_point(int x, int c)
{
Max[N + x] = c;
int i = N + x;
for(i >>= 1; i; i >>= 1)
{
Max[i] = max(Max[i << 1], Max[i << 1 | 1]);
}
}
int Query(int s, int e)
{
int L,R,mx = -1;
for(L = N + s - 1,R = N + e + 1; L ^ R ^ 1; L >>= 1, R >>= 1)
{
if(~ L & 1) mx = max(mx, Max[L ^ 1]);
if( R & 1) mx = max(mx, Max[R ^ 1]);
}
return mx;
} int main()
{
ios::sync_with_stdio(false); cin.tie(nullptr);
//freopen("T.txt","r",stdin);
int n,m;
while(cin >> n >> m)
{
for(int i = 1; i <= n; i ++)
cin >> node[i];
Build(n);
char ch;
int a, b;
while(m --)
{
cin >> ch >> a >> b;
if(ch == 'Q')
cout << Query(a, b) << endl;
else
Update_point(a, b);
}
} return 0;
}

HDU1166(线段树 +更新单点,求区间总和)、HDU1754(线段树 + 更新单点,求区间最大值)的更多相关文章

  1. HDU(1166),线段树模板,单点更新,区间总和

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1166 第一次做线段树,帆哥的一句话,我记下来了,其实,线段树就是一种处理数据查询和更新的手段. 然后, ...

  2. CSU-1110 RMQ with Shifts (单点更新+区间最小值 zkw线段树)

    In the traditional RMQ (Range Minimum Query) problem, we have a static array A. Then for each query ...

  3. HDU.1689 Just a Hook (线段树 区间替换 区间总和)

    HDU.1689 Just a Hook (线段树 区间替换 区间总和) 题意分析 一开始叶子节点均为1,操作为将[L,R]区间全部替换成C,求总区间[1,N]和 线段树维护区间和 . 建树的时候初始 ...

  4. POJ 2528 Mayor's posters(线段树区间染色+离散化或倒序更新)

    Mayor's posters Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 59239   Accepted: 17157 ...

  5. HDU 1540 Tunnel Warfare(经典)(区间合并)【线段树】

    <题目链接> 题目大意: 一个长度为n的线段,下面m个操作 D x 表示将单元x毁掉 R  表示修复最后毁坏的那个单元 Q x  询问这个单元以及它周围有多少个连续的单元,如果它本身已经被 ...

  6. HDU 3308 LCIS (经典区间合并)【线段树】

    <题目链接> 题目大意: 给你一段序列,对其进行两种操作,一是修改某个序号的点的值:二是查询某个区间的LCIS(最长上升子序列). 解题分析: 线段树区间合并的典型例题,用求某个区间的LC ...

  7. HDU1255 覆盖的面积 —— 求矩形交面积 线段树 + 扫描线 + 离散化

    题目链接:https://vjudge.net/problem/HDU-1255 给定平面上若干矩形,求出被这些矩形覆盖过至少两次的区域的面积. Input输入数据的第一行是一个正整数T(1<= ...

  8. 计蒜客 38229.Distance on the tree-1.树链剖分(边权)+可持久化线段树(区间小于等于k的数的个数)+离散化+离线处理 or 2.树上第k大(主席树)+二分+离散化+在线查询 (The Preliminary Contest for ICPC China Nanchang National Invitational 南昌邀请赛网络赛)

    Distance on the tree DSM(Data Structure Master) once learned about tree when he was preparing for NO ...

  9. HDU1542 Atlantis —— 求矩形面积并 线段树 + 扫描线 + 离散化

    题目链接:https://vjudge.net/problem/HDU-1542 There are several ancient Greek texts that contain descript ...

随机推荐

  1. 《一步步成为 Hacker_Day 01》

    环境搭建 传统运行模式 - 一台机器同时只能运行一个操作系统 |:----------|----------:| | 应用程序 | 应用程序 | |:----------|----------:| | ...

  2. Eureka停更了?试试Zookpper和Consul

    在Spring Cloud Netflix中使用Eureak作为注册中心,但是Eureka2.0停止更新,Eureka1.0 进入了维护状态.就像win7一样,同样可以用,但是官方对于新出现的问题并不 ...

  3. 一文掌握Redis的三种集群方案

    在开发测试环境中,我们一般搭建Redis的单实例来应对开发测试需求,但是在生产环境,如果对可用性.可靠性要求较高,则需要引入Redis的集群方案.虽然现在各大云平台有提供缓存服务可以直接使用,但了解一 ...

  4. requests.exceptions.SSLError报错

    requests.exceptions.SSLError: HTTPSConnectionPool(host='www.baidu.com', port=443): Max retries excee ...

  5. 2020年启蒙及小学识字练字APP或小程序测评榜

    语文教学改革后,小学识字练字方面显得越来越重要.而市场上大大小小的识字练字应用琳琅满目,不同的定位,不同的核心功能,不同的费用.应该怎么选呢? 本篇将从多个角度对主流识字练字应用进行评测,评估对象为主 ...

  6. 【Weiss】【第04章】AVL树例程

    普通的二叉搜索树可能会由于数据不平均.删除产生高度差等原因,使树倾向于不平衡生长,导致操作慢于O(NlogN). 为应对此现象,将搜索.删除.插入的最坏时间也控制在O(NlogN)上,产生了平衡二叉树 ...

  7. 第三届上海市大学生网络安全大赛 流量分析 WriteUp

    题目链接: https://pan.baidu.com/s/1Utfq8W-NS4AfI0xG-HqSbA 提取码: 9wqs 解题思路: 打开流量包后,按照协议进行分类,发现了存在以下几种协议类型: ...

  8. Python Django撸个WebSSH操作Kubernetes Pod

    优秀的系统都是根据反馈逐渐完善出来的 上篇文章介绍了我们为了应对安全和多分支频繁测试的问题而开发了一套Alodi系统,Alodi可以通过一个按钮快速构建一套测试环境,生成一个临时访问地址,详细信息可以 ...

  9. POJ1523 Tarjan求割点以及删除割点之后强连通分量的数量

    题目链接:http://poj.org/problem?id=1523 SPF:A Single Point of Failure也就是割点(一个点导致网络之间的不连通),由于给出的图是无向图,所以只 ...

  10. JDBC(三)----Spring JDBC(JDBCTemplate)

    ##  Spring  JDBC *  Spring框架对JDBC的简单封装.提供了一个JDBCTemplate对象简化JDBC的开发 1.步骤 1.导入jar包 2.创建JDBCTemplate对象 ...