线段树简单应用

先附上几张图便与理解,大佬文章传送门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. IDEA Messages Build总是自动弹出提示错误

    IDEA,总是在代码未完成时,在进行切换页面回来后会跳出Messages Build,我相信大家在写java web项目的时候,经常会遇到这个问题,接下来我就和大家说一下问题所在. 主要原因是因为我们 ...

  2. 关于独立部署web项目到tomcat服务器详情

    步骤: 1.设置端口号:找到所解压的tomcat的目录下中的conf文件夹,再用editPlus打开conf文件夹中的server.xml文件,tomcat初始端口为8005,8080,8009,如果 ...

  3. 后台管理遇到的坑一、style中css样式怎么传入变量值

    第一.给标签定义style变量 第二.在data中定义 第三.在methods中的方法中给样式赋值

  4. javascript的constructor属性介绍

    之前闲来了解了__proto__和prototype的区别,每个对象都有隐私属性__proto__,而prototype是javascript函数特有的属性.那么constructor属性呢?最近是遇 ...

  5. (28)ASP.NET Core AutoMapper组件

    1.什么是AutoMapper? AutoMapper是一个对象-对象映射器.对象-对象映射通过将一种类型的输入对象转换为另一种类型的输出对象来工作.使AutoMapper变得有趣的是,它提供了一些有 ...

  6. Vue2.0 【第一季】第1节 走进我的Vue2.0

    目录 Vue2.0 [第一季]内部指令 第一节 走进我的Vue2.0 Vue2.0 [第一季]内部指令 记录一下我的代码地址:D:/Code/Vue 编辑器:VS code 前置知识: 1.HTML的 ...

  7. js小数计算引起的精度误差问题

    我记得刚开始学js的时候学到浮点有举例0.1+0.2 它的计算结果是: 0.1+0.20.30000000000000004 很神奇的一个计算,js是弱语言,在精度上没做处理: 我就自己定义了加减乘除 ...

  8. airtest的使用(作用类似于appium)

    1.安装 下载免安装版本:http://airtest.netease.com/ 文档:http://airtest.netease.com/docs/docs_AirtestIDE-zh_CN/in ...

  9. 你知道吗,Flutter内置了10多种show

    注意:无特殊说明,Flutter版本及Dart版本如下: Flutter版本: 1.12.13+hotfix.5 Dart版本: 2.7.0 showDialog showDialog 用于弹出Mat ...

  10. 升级cocoapods到指定版本

    把cocoapods升级到1.7.4 sudo gem install -n /usr/local/bin cocoapods -v 1.7.4