线段树是二叉搜索树,每个节储存一个区间的信息。,通常适合用来处理区间的问题
线段树的基本思想是二分,线段树的核心是懒标记
1、区间修改&&区间查询
题解 P3372 【【模板】线段树 1】 - 菜鸡本菜 - 洛谷博客 (luogu.com.cn)
单点修改是区间修改的特例
2、两个懒标记
P3373 【模板】线段树 2 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
先乘后加,懒标记在pushdown过程中顺序很重要
3、扫描线
P5490 【模板】扫描线 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
扫描线可用于处理面积并。通过垂直x轴的的扫描线从图形最左向最右扫描,扫描过程中要不断维护沿y轴上的有效长度,即值域内哪些线段对计算面积有贡献,而有效长度可看作一个个有贡献的区间内的线段的和,所以有效长度可利用线段树来维护。
注意:由于数据在y轴上范围过大,所以要对数据进行离散化,有效长度也用离散线段树维护(离散线段树叶节点长度为2)
具体参考:
576 扫描线算法 线段树【计算几何】_哔哩哔哩_bilibili
4、
P4588 [TJOI2018]数学计算 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
该题用线段树来维护区间乘,这样的话根节点就是到现在为止的所有数的乘积。
由于数据过大的原因,模拟的话sum* m可能会让结果直接溢出,所以我们可以考虑所有乘数在相乘的过程中进行取模,相当于(m1*m2*m3*m4……)%mod转变为m1*m2%mod*m3%mod*m3……,即乘以次取一次模,可以用在线段树pushup的操作上。
第二个操作是去除某次乘的值,根据乘除逆运算的关系,除一个值就是将那个值置1,可以看作一次修改操作,即修改叶节点。
5、区间最大
P2471 [SCOI2007] 降雨量 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
线段树维护区间最大值
int query(int now, int l, int r, int x, int y) {
if (x <= l && r <= y) return t[now];
int mid = (l + r) / 2;
int ans = 0;
if (x <= mid) ans = query(ls(now), l, mid, x, y);
if (y > mid) ans = max(ans, query(rs(now), mid + 1, r, x, y));
// 注意不是 else if(y > mid) ans = max(ans, query(rs(now), mid + 1, r, x, y)), 否则有可能
// 会缺少对右区间的查询
return ans;
}
须注意点:
(1)离散化:low_bound的使用,返回值就是返回第一次出现大于等于那个要查找的数的地址。
(2)输入年份先判断其降雨量是否存在,再判断两个年份(下标)是否相邻,最后再判断区间最大。
6、懒标记标记状态
P2574 XOR的艺术 - 洛谷 | 计算机科学教育新生态 (luogu.com.cnl
懒标记传递反转状态,需要反转为1,不需要为0。
7、线段树维护两值
P1471 方差 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
方差可拆成区间平方和与区间和的线性组合,所以线段树要同时维护区间平方和与区间和。
8、线段树维护区间最大线段
Glass Carving - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
线段树叶节点表示矩形内直线是否被分割,1表示未分割,0表示已分割,则统计连续1最大长度+1即为最大线段。
判断连续1最大长度时,注意最大长度可能出现一个节点所代表区间中间,即左儿子节点右端连续1长度+右儿子节点左端连续1长度,所以每个节点要维护左端最大连续1长度、右端最大连续1长度和整个区间最大连续1长度。
特判:儿子区间全为连续1,节点的左端最大连续1长度或右端最大连续1长度的维护有不同处理。
P2894 [USACO08FEB] Hotel G - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
线段树维护区间连续最大线段,每个节点同样要维护左端连续最大线段、中间连续最大线段、右端连续最大线段,所以懒标记的传递和向上的更新函数写法与常规不同。
此题精髓在查询,左儿子有符合线段则一直向左递归,直到左儿子不够长,进而判断左儿子与右儿子连续线段是否符合,若都不符合则向右儿子递归。
int ask(int p,int l,int r,int length)
{
spread(p);//下放懒标记
if(l==r)return l;//如果找到对应区间,返回左端点
int mid=(l+r)/2;
if(t[p*2].sum>=length)return ask(p*2,l,mid,length);
//如果左区间即可找到足够多的房间,就在左区间找
if(t[p*2].rmax+t[p*2+1].lmax>=length)return mid-t[p*2].rmax+1;
//如果在中间能找到足够多的房间,答案就是左区间从右开始的最长连续区间的左端点
else return ask(p*2+1,mid+1,r,length);
//否则就在右边找
}
9、维护区间最大公约数