线段树
一:一般功能
二:基本概念
三:基本操作
一:基本功能
线段树的一般功能为定点修改,区间修改,区间求和,求区间最大值等操作。当然也有一些扩展的,例如主席树一类的。不过关于扩展我还不太了解,现在先讲一下一般功能,日后再更新。
二:基本概念
1:它是一个二叉树结构
2:它的每一个节点存一个结构体,每个结构体一般包括它的左右边界,还有它的权值一类的,还要看题具体定
3:它用到的思想为二分
4:它的结构如下图
例如第一个节点包含区间1-10的信息,如果是求区间和,那么就是存的1-10的所有值的和,它又可以分为两个左右区间,知道当它的左右边界相等时便是一个数据。
三:基本操作
1:定义子节点结构体
在这里插入代码片struct node
{
int l, r, w, flag;//其中l,r表示这个区间的左右边界,w表示这个区间的权值,flag表示如要改变这个区间,它就是改变的值
int dis() { return r - l + 1; }//这点所代表区间的个数
int mid() { return (r + l) / 2; }//区间的中间值,便于将这个区间分割
} a[maxn * 4];
2:建树
在这里插入代码片void build(int k, int l, int r)\\建树,就是向每一个子节点里赋值
{ //当前节点的区间
a[k] = {l, r, 0, 0};//对结构体赋值
if (l == r)
{
cin >> a[k].w;//当这是一个点时,赋值
return;
}
build(k << 1, l, a[k].mid());//将区间往下分
build(k << 1 | 1, a[k].mid() + 1, r);
a[k].w = a[k << 1].w + a[k << 1 | 1].w;//一个区间和等于左边的加右边的
}
3:区间修改,也可用于单点修改
void down(int k)//主要是将这个区间改变的值向下分
{
a[k << 1].w += a[k << 1].dis() * a[k].flag;
a[k << 1 | 1].w += a[k << 1 | 1].dis() * a[k].flag;
a[k << 1].flag += a[k].flag;
a[k << 1 | 1].flag += a[k].flag;
a[k].flag = 0;
}
void update(int k, int l, int r, int w)
{ // 要更新的总区间.(l,r)不变
if (a[k].l >= l && a[k].r <= r)//这句主要判断你要改变的区间是否大于你查询的这个,如果大于,直接更改区间即可
{
a[k].w += w * a[k].dis();//将这个区间加一个数,如果直接改为一个数,直接写等于即可。
a[k].flag += w;
return;
}
if (a[k].flag)
down(k);
if (a[k].mid() >= l)
update(k << 1, l, r, w);
if (a[k].mid() < r)
update(k << 1 | 1, l, r, w);
a[k].w = a[k << 1].w + a[k << 1 | 1].w;
}
4:区间求和
int query(int k, int l, int r)//求区间的和
{
if (a[k].l >= l && a[k].r <= r)
return a[k].w;
if (a[k].flag)//这句是判断这个区间是否被改变过,如果改变过,应该将改变的值下方到区间的每一个元素
down(k);
int sum = 0;
if (a[k].mid() >= l)//这主要是缩小区间去寻找满足条件的区间
sum += query(k << 1, l, r);
if (a[k].mid() < r)
sum += query(k << 1 | 1, l, r);
a[k].w = a[k << 1].w + a[k << 1 | 1].w;
return sum;
}
5:全部代码
包括建树,区间求和,区间修改,单点修改,如果要去区间的最大值只需要稍微修改一下,将每一个数存的不是区间的和而是存区间的最大值
#include <bits/stdc++.h>
using namespace std;
#define mem(a, b) memset(a, b, sizeof a)
#define IN freopen("in.txt", "r", stdin)
#define DEBUG(a) cout << (a) << endl
typedef long long ll;
int dir8[8][2] = {{1, 0}, {0, 1}, {-1, 0}, {0, -1}, {1, 1}, {1, -1}, {-1, 1}, {-1, -1}};
int dir4[4][2] = {1, 0, 0, 1, -1, 0, 0, -1};
const int INF = 0x3f3f3f3f;
int mod = 1e9 + 7;
const int maxn = 1e5 + 10;
struct node
{
int l, r, w, flag;
int dis() { return r - l + 1; }
int mid() { return (r + l) / 2; }
} a[maxn * 4];
void build(int k, int l, int r)
{ //当前节点的区间
a[k] = {l, r, 0, 0};
if (l == r)
{
cin >> a[k].w;
return;
}
build(k << 1, l, a[k].mid());
build(k << 1 | 1, a[k].mid() + 1, r);
a[k].w = a[k << 1].w + a[k << 1 | 1].w;
}
void down(int k)
{
a[k << 1].w += a[k << 1].dis() * a[k].flag;
a[k << 1 | 1].w += a[k << 1 | 1].dis() * a[k].flag;
a[k << 1].flag += a[k].flag;
a[k << 1 | 1].flag += a[k].flag;
a[k].flag = 0;
}
void update(int k, int l, int r, int w)
{ // 要更新的总区间.(l,r)不变
if (a[k].l >= l && a[k].r <= r)
{
a[k].w += w * a[k].dis();
a[k].flag += w;
return;
}
if (a[k].flag)
down(k);
if (a[k].mid() >= l)
update(k << 1, l, r, w);
if (a[k].mid() < r)
update(k << 1 | 1, l, r, w);
a[k].w = a[k << 1].w + a[k << 1 | 1].w;
}
int query(int k, int l, int r)
{
if (a[k].l >= l && a[k].r <= r)
return a[k].w;
if (a[k].flag)
down(k);
int sum = 0;
if (a[k].mid() >= l)
sum += query(k << 1, l, r);
if (a[k].mid() < r)
sum += query(k << 1 | 1, l, r);
a[k].w = a[k << 1].w + a[k << 1 | 1].w;
return sum;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n, m;
cin >> n >> m;
build(1, 1, n);
while (m--)
{
int func;
int l, r, w;
cin >> func;
if (func == 1)
{
cin >> l >> r >> w;
update(1, l, r, w);
}
else if (func == 2)
{
cin >> l >> r;
cout << query(1, l, r) << endl;
}
}
return 0;
}