[多重解法]线段树练习(动态求连续区间和,区间最大值)
目录
线段树目标:求任意动态区间元素的最值or总和
场景描述:
给定了一个数组, 接下来求指定区间的最值或总和,最简单的做法便是直接遍历该区间, 但数组可能非常大或者会对其中的数再做修改,每次都暴力会超时
线段树满足完全二叉树的性质,能将时间复杂度降到O()级别
例1:
输入样例:
10 2
3 2 4 5 6 8 1 2 9 7
1 4
3 8
输出样例:
5
8
写法一(线段树):
运行时间: 3125 ms
#include <iostream>
#include <cstring>
#include <climits>
#include <algorithm>
using namespace std;
const int N = 1e5+10;
int w[N];
struct tree{
int l, r;
int _max;
}tr[4*N];
//建树
void build(int u, int l, int r)
{
if(l == r) //叶节点
tr[u] = {l, r, w[l]};
else{
tr[u] = {l, r};
int mid = l+r >> 1;
//递归建立左右子树, 并在归来之时更新当前节点
build(u << 1, l, mid), build(u << 1 | 1, mid+1, r);
tr[u]._max = max(tr[u<<1]._max, tr[u<<1|1]._max);
}
}
//查询指定区间[l, r]
int query(int u, int l, int r)
{
//当前节点所代表的区间是所求区间的子集
if(tr[u].l>=l && tr[u].r<=r) return tr[u]._max;
int mid = tr[u].l+tr[u].r >> 1;
int res = INT_MIN;
//当前区间有一部分在当前节点所代表区间的左子区间
if(mid >= l) res = query(u << 1, l, r);
//当前区间有一部分在当前节点所代表区间的右子区间
if(mid < r) res = max(res, query(u<<1|1, l, r));
return res;
}
int main()
{
int n, m;
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i ++) scanf("%d", &w[i]);
build(1, 1, n);
while (m -- )
{
int l, r;
scanf("%d%d", &l, &r);
printf("%d\n", query(1, l, r));
}
return 0;
}
写法二(ST表):
相关题型:
运行时间: 1402 ms
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const int N = 1e5+10, M = 18;
int n, m;
int f[N][M], w[N];
int search(int l, int r)
{
int len = r-l+1;
int k = log(len)/log(2);
return max(f[l][k], f[r-(1<<k)+1][k]);
}
int main()
{
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i ++) scanf("%d", &f[i][0]);
for(int j = 1; j < M; j ++)
for(int i = 1; i+(1<<j)-1 <= n; i ++)
f[i][j] = max(f[i][j-1], f[i+(1<<j-1)][j-1]);
while(m --)
{
int l,r;
scanf("%d%d", &l, &r);
printf("%d\n", search(l, r));
}
}
写法三(树状数组):
即时插点和递归查询
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1e5+10;
int nums[N], bit[N], n, m;
inline int lowbit(int x) {
return x & -x;
}
void add(int x) {
for (int i = x; i <= n; i += lowbit(i)) {
bit[i] = nums[i];
for (int j = 1; j < lowbit(i); j <<= 1)
bit[i] = max(bit[i], bit[i - j]);
}
}
int query(int l, int r) {
if (l == r) return nums[r];
if (l == r - lowbit(r) + 1) return bit[r];
if (l < r - lowbit(r) + 1) return max(bit[r], query(l, r - lowbit(r)));
else return max(nums[r], query(l, r - 1));
}
int main()
{
int l, r;
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; i ++) {
scanf("%d", &nums[i]);
add(i);
}
while (m --)
{
scanf("%d %d", &l, &r);
printf("%d\n", query(l, r));
}
return 0;
}
建树和递推查询
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1e5+10;
int nums[N], bit[N], n, m;
inline int lowbit(int x) {
return x & -x;
}
// 初始化树状数组
void build() {
for (int i = 1; i <= n; ++ i) {
bit[i] = nums[i];
for (int j = 1; j < lowbit(i); j <<= 1)
bit[i] = max(bit[i], bit[i - j]);
}
}
// 区间查询
int query(int l, int r) {
int maxv = INT_MIN;
while (l <= r) {
maxv = max(maxv, nums[r]);
r --;
for (; l <= r - lowbit(r); r -= lowbit(r))
maxv = max(maxv, bit[r]);
}
return maxv;
}
int main()
{
int l, r;
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; i ++) scanf("%d", &nums[i]);
build();
while (m --) {
scanf("%d %d", &l, &r);
printf("%d\n", query(l, r));
}
return 0;
}
例2:
输入样例:
10 5
1 2 3 4 5 6 7 8 9 10
1 1 5
0 1 3
0 4 8
1 7 5
0 4 8
输出样例:
11
30
35
写法一(线段树):
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e5+10;
int w[N];
struct tree{
int l, r;
int sum;
}tr[4 * N];
void build(int u, int l, int r)
{
if (l == r) tr[u] = {l, r, w[l]};
else
{
tr[u] = {l, r};
int mid = l + r >> 1;
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
}
}
int query(int u, int l, int r)
{
if (tr[u].l >= l && tr[u].r <= r) return tr[u].sum;
int mid = tr[u].l + tr[u].r >> 1;
int res = 0;
if (l <= mid) res += query(u << 1, l, r);
if (r > mid) res += query(u << 1 | 1, l, r);
return res;
}
void modify(int u, int x, int v)
{
if(tr[u].l == tr[u].r) tr[u].sum += v;
else{
int mid = tr[u].l + tr[u].r >> 1;
if(x <= mid) modify(u << 1, x, v);
else modify(u << 1 | 1, x, v);
tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
}
}
int main()
{
int n, m;
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i ++) scanf("%d", &w[i]);
build(1, 1, n);
int k, a, b;
while (m -- )
{
scanf("%d%d%d", &k, &a, &b);
if(k) modify(1, a, b);
else printf("%d\n", query(1, a, b));
}
return 0;
}
写法二(树状数组):
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e5+10;
int n;
int s[N];
int lowbit(int x) {
return x & -x;
}
void add(int x, int d){
for(int i = x; i <= n; i += lowbit(i)) s[i] += d;
}
int search(int x){
int res = 0;
for(int i = x; i; i -= lowbit(i)) res += s[i];
return res;
}
int main()
{
int m;
scanf("%d%d", &n, &m);
int x;
for(int i = 1; i <= n; i ++){
scanf("%d", &x);
add(i, x);
}
int k, a, b;
for (int i = 0; i < m; i ++ )
{
scanf("%d%d%d", &k, &a, &b);
if(k) add(a, b);
else printf("%d\n", search(b)-search(a-1));
}
return 0;
}
本文来自博客园,作者:泥烟,CSDN同名, 转载请注明原文链接:https://www.cnblogs.com/Knight02/p/16006965.html