概述
线段树,一种实用的数据结构,可以实现单点更新,区间更新,区间查询,时间复杂度都是log级的。
线段树是拿数组模拟二叉树,树上的每个节点保存区间的信息。
今天写了两道单点更新,区间查询的例题。
hdu 1166 Problem - 1166
本题之所以可以用线段树,原因之一是单点更新,区间查询,非常适合线段树这一数据结构,其次是满足线段树查询的条件:符合区间加法。
另外要注意线段树数组的大小是n的四倍,然而开小了oj上的报错是TLE而不是RE。
// Problem: 敌兵布阵
// Contest: HDOJ
// URL: http://acm.hdu.edu.cn/showproblem.php?pid=1166
// Memory Limit: 65 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
using namespace std;
const int N = 50005;
int a[N];
// struct node
// {
// int val;
// // int lazy; 单点更新不需要懒惰标记
// }
int segTree[4 * N + 500];
void pushUp(int rt)
{
segTree[rt] = segTree[rt * 2] + segTree[rt * 2 + 1];
}
void build(int l, int r, int rt)
{
if (l == r)
{
segTree[rt] = a[l];
return;
}
int m = (l + r) / 2; //二分递归建左右子树
build(l, m, rt * 2);
build(m + 1, r, rt * 2 + 1);
pushUp(rt); //回溯建父节点
}
void upDate(int index, int add, int l, int r, int rt)
{
if (l == r)
{
segTree[rt] += add;
return;
}
int m = (l + r) / 2; //通过二分查找下标
if (index <= m) //下标位于左子树,更新左子树
{
upDate(index, add, l, m, rt * 2);
}
else
upDate(index, add, m + 1, r, rt * 2 + 1);
pushUp(rt); //回溯更新父节点
}
int query(int L, int R, int l, int r, int rt)
{
if (L <= l && r <= R)
{
return segTree[rt];
}
if (L > r || R < l)
return 0;
int m = (l + r) / 2;
return query(L, R, l, m, rt * 2) + query(L, R, m + 1, r, rt * 2 + 1);
}
int main()
{
int t;
cin >> t;
for (int count = 1; count <= t; count++)
{
printf("Case %d:n", count);
int n;
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
}
build(1, n, 1);
char s[50];
while (~scanf("%s", s))
{
if (!strcmp(s, "End"))
break;
else if (s[0] == 'A')
{
int index, add;
cin >> index >> add;
upDate(index, add, 1, n, 1);
}
else if (s[0] == 'S')
{
int index, add;
cin >> index >> add;
add = 0 - add;
upDate(index, add, 1, n, 1);
}
else if (s[0] == 'Q')
{
int L, R;
cin >> L >> R;
int ans = query(L, R, 1, n, 1);
printf("%dn", ans);
}
}
}
}
详解见此博客线段树详解 (原理,实现与应用)_岩之痕的博客-CSDN博客_线段树
hdu 1754 Problem - 1754
本题同样也是单点更新,区间查询。并且查询区间的最大值是符合区间加法的。
本题询问区间最大值,那么只需要在建树的时候用父节点保存子节点的最大值即可,也就是说我们的回溯函数pushup,不再是子区间求和,而是子区间比大小。还有就是根据题意,修改单点的时候是赋值。
还有就是要注意字符的读入,实话说这块我也不是很清楚。
// Problem: I Hate It
// Contest: HDOJ
// URL: http://acm.hdu.edu.cn/showproblem.php?pid=1754
// Memory Limit: 32 MB
// Time Limit: 9000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
using namespace std;
const int N = 200005;
int a[N];
int segTree[N * 4 + 500];
void pushUp(int rt)
{
segTree[rt] = max(segTree[rt * 2], segTree[rt * 2 + 1]);
}
void build(int l, int r, int rt)
{
if (l == r)
{
segTree[rt] = a[l];
return;
}
int m = (l + r) / 2;
build(l, m, rt * 2);
build(m + 1, r, rt * 2 + 1);
pushUp(rt);
}
void upDate(int index, int add, int l, int r, int rt)
{
if (l == r)
{
segTree[rt] = add;
return;
}
int m = (l + r) / 2; //通过二分查找下标
if (index <= m) //下标位于左子树,更新左子树
{
upDate(index, add, l, m, rt * 2);
}
else
upDate(index, add, m + 1, r, rt * 2 + 1);
pushUp(rt); //回溯更新父节点
}
int query(int L, int R, int l, int r, int rt)
{
if (L <= l && r <= R)
{
return segTree[rt];
}
if (L > r || R < l)
return 0;
int m = (l + r) / 2;
return max(query(L, R, l, m, rt * 2), query(L, R, m + 1, r, rt * 2 + 1));
}
int main()
{
int n, m;
while (~scanf("%d %d", &n, &m))
{
for (int i = 1; i <= n; i++)
{
scanf("%d", &a[i]);
}
build(1, n, 1);
while (m--)
{
char ch[2];
scanf("%s", ch);
if (ch[0] == 'Q')
{
int l, r;
scanf("%d %d", &l, &r);
int ans = query(l, r, 1, n, 1);
printf("%dn", ans);
}
if (ch[0] == 'U')
{
int index, num;
scanf("%d %d", &index, &num);
upDate(index, num, 1, n, 1);
}
// getchar();
}
}
}
最后
以上就是追寻灰狼为你收集整理的9/21 线段树模板 单点更新 区间查询的全部内容,希望文章能够帮你解决9/21 线段树模板 单点更新 区间查询所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复