线段树
线段树
具体例题
区间加法和区间求和: 记录详情 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
区间加法乘法,求和 记录详情 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
基本概念
1、线段树是一棵二叉搜索树,它储存的是一个区间的信息。
2、每个节点以结构体的方式存储,结构体包含以下几个信息:
区间左端点、右端点;(这两者必有)
这个区间要维护的信息(事实际情况而定,数目不等)。
3、线段树的基本思想:二分。
形式如下:
结构体------要开4倍的空间
struct node
{
long long sum, add;
int left, right;
};//左右节点,区间和,懒标记,
或者
struct node
{
long long sum, add, mul;
int left, right;
}; //支持乘法操作,mul(乘法的懒标记)
线段树支持的操作有:
1,建树,即建立一棵线段树
//create(1,n,1);//将n个数建立成线段树
void create(int l, int r, int i)
{
a[i].left = l;
a[i].right = r;
a[i].sum = 0;
a[i].add = 0;
if (l == r)
{
cin >> a[i].sum;
return;
}
int mid = (l + r) / 2;
create(l, mid, i << 1);
create(mid + 1, r, (i << 1) + 1);
a[i].sum = a[i << 1].sum + a[(i << 1) + 1].sum;
}
2,单点查询,即查询一个点的状态,设待查询点为x
int query(int x, int i)//x为要查询的第x个数
{
if (a[i].left == a[i].right && x == a[i].left)
{
return a[i].sum;
}
push_down(i);//区间被修改时使用
int mid = (a[i].left + a[i].right) / 2;
if (x <= mid)
query(x, i << 1);
else
query(x, i << 1 | 1);
}
3、单点修改,即更改某一个点的状态,即对第x个数加上y
void update(int i, int x, int y)让第x个数加上y
{
if (a[i].left == a[i].right && a[i].left == x)
{
a[i].sum += y;
return;
}
push_down(i);
int mid = (a[i].left + a[i].right) / 2;
if (x <= mid)
update(i << 1, x, y);
else
update(i << 1 | 1, x, y);
a[i].sum = a[i << 1].sum + a[i << 1 | 1].sum;//向上回溯
}
4、区间查询,即查询一段区间的状态
void query(int l, int r, int i) //查询[l,r]区间的和
//ans为全局变量
{
if (a[i].left == l && a[i].right == r)
{
ans += a[i].sum;
return;
}
push_down(i);
int mid = (a[i].left + a[i].right) / 2;
if (l > mid)
query(l, r, (i << 1) + 1);
else if (r <= mid)
query(l, r, i << 1);
else
{
query(l, mid, i << 1);
query(mid + 1, r, (i << 1) + 1);
}
}
5、区间修改,即修改一段连续区间的值
void update(int l, int r, int i, long long k) //区间[l,r]所有数加上k
{
if (a[i].left == l && a[i].right == r)
{
a[i].sum += k * (r - l + 1);
a[i].add += k;
return;
}
push_down(i);
int mid = (a[i].left + a[i].right) / 2;
if (r <= mid)
update(l, r, i << 1, k);
else if (l > mid)
update(l, r, (i << 1) + 1, k);
else
{
update(l, mid, i << 1, k);
update(mid + 1, r, (i << 1) + 1, k);
}
a[i].sum = a[i << 1].sum + a[(i << 1) + 1].sum;
}
懒标记下传函数
void push_down(int i)
{
if (a[i].add != 0)
{
a[i << 1].add += a[i].add;
a[(i << 1) + 1].add += a[i].add;
a[i << 1].sum += a[i].add * (a[i << 1].right - a[i << 1].left + 1);
a[(i << 1) + 1].sum += a[i].add * (a[(i << 1) + 1].right - a[(i << 1) + 1].left + 1);
a[i].add = 0;
}
}
区间加法和乘法混合时;
区间乘上x时,乘法懒标记mui*=x;加法懒标记add*=x;
区间加上x时,加法懒标记add+=x;
向下传递时:push_down(int i)
先计算乘法,后计算加法,最后add清零,mul变成1