线段树简单应用

先附上几张图便与理解,大佬文章传送门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. SQLi-Labs之1~6关 - 常规注入与盲注

    年初五 财神入 第一关 联合注入 1.准备 2.加'截断 3.order by 判断查询列数 4.同上 5.联合查询判断字段位置 6.查数据库名 7.1 查表名 7.2 查列名 8.查数据 第二关 不 ...

  2. Django Queryset增加manager

    **#定义一个新的过滤规则,这里是过滤状态为发布的帖子** **class PublishedManager(models.Manager):**        **def get_queryset( ...

  3. 实现Sobel算子滤波、Robers算子滤波、Laplace算子滤波

    前几天,老师布置了这样一个任务,读取图片并显示,反色后进行显示:进行Sobel算子滤波,然后反色,进行显示:进行Robers算子滤波,然后反色,进行显示.我最后加上了Laplace算子滤波,进行了比较 ...

  4. selenium+options配置文件

    from selenium.webdriver.chrome.options import Options from selenium import webdriver chrome_options ...

  5. ASP.NET Core ActionFilter引发的一个EF异常

    最近在使用ASP.NET Core的时候出现了一个奇怪的问题.在一个Controller上使用了一个ActionFilter之后经常出现EF报错. InvalidOperationException: ...

  6. Python只有文件不存在才能写文件

    当我们在Python里面写文件时,我们常用的模式为 w模式,这种模式下,如果文件不存在,就会生成文件:如果文件已经存在,就会直接覆盖. 有时候,如果文件已经存在,直接覆盖文件可能会导致重要数据丢失.你 ...

  7. 简说Python之flask-SQLAlchmey的web应用

    目录 原生语句操作MySQL数据库 1.安装MySQL 2.MySQL设置用户和权限 3.用PyMySQL操纵MySQL数据库 4. CRUD增,删,改,查 使用SQLAlchemy 1.安装SQLA ...

  8. 【牛客】乃爱与城市拥挤程度 — 树形dp,up and down

    我太难了 这题做得我要死了,来来回回写了大概八九个小时 错误的原因要么是快速幂写错(一生之敌,要么是忘取模爆\(longlong\)变负数\(QAQ\) \(update\) \(2019.11.13 ...

  9. 前端构建工具gulpjs的使用介绍及技巧【转载】

    转载至:http://www.cnblogs.com/2050/p/4198792.html gulpjs是一个前端构建工具,与gruntjs相比,gulpjs无需写一大堆繁杂的配置参数,API也非常 ...

  10. CF1326C Permutation Partitions 题解,

    原题链接 简要题意: 给定一个 \(1\) ~ \(n\) 的置换,将数组分为 \(k\) 个区间,使得每个区间的最大值之和最大.求这个值,和分区的方案数. 关键在于 \(1\) ~ \(n\) 的置 ...