好的博客:笨蛋花的小窝qwq
一、什么是线段树
- 线段树是表示区间及线段的树
什么是区间,什么又是线段呢?
这里有图
- 这样的一棵树,可以解决区间的覆盖问题。
例题
- 输入m条线段,问这m条线段被覆盖的面积有多大。
Sample Input
1 2
3 4
1 7
2 13
Sample Output
13
那么这道题怎么做呢?
方法一:模拟(容易爆)
方法二:
首先,由于1-13这个区间跨越了“两岸”,所以我们把它分成1-8和9-13。
而9-13这个区间又跨越了“两岸”,分成9-12和13-13。
最后,这三个区间都可以直接求出来,就OK啦~
- 自己实验一道题——洛谷 P1047 校门外的树
代码:
#include<iostream>
using namespace std;
struct Node
{
int lson,rson,cnt;//cnt代表这个区间的树的棵树
}tree[40040];
int l,m;
int ql,qr;
void build_tree (int now,int nl,int nr)
{
tree[now].lson = nl;
tree[now].rson = nr;//记录区间左端点和右端点
if (nl == nr)
{
tree[now].cnt = 1;//若这个区间只有一个点,cnt值为1
return ;
}
int mid = (nl + nr) / 2;//取中点
build_tree (now * 2,nl,mid);
build_tree (now * 2 + 1,mid + 1,nr);//递归调用
tree[now].cnt = tree[now * 2].cnt + tree[now * 2 + 1].cnt;
//这个区间的cnt值为它的两个儿子cnt值的和
}
void cover (int now,int nl,int nr,int ql,int qr)
{
if (nl > qr || nr < ql || !tree[now].cnt) return ;
//若现区间与询问区间完全错开,则该区间不影响
if (ql <= nl && qr >= nr)
{
tree[now].cnt = 0;//若现区间被询问区间包含,树被砍光
return ;
}
int mid = (nl + nr) / 2;
cover (now * 2,nl,mid,ql,qr);
cover (now * 2 + 1,mid + 1,nr,ql,qr);//递归调用
tree[now].cnt = tree[now * 2].cnt + tree[now * 2 + 1].cnt;
//同上
}
int main()
{
cin >> l >> m;//输入
build_tree(1,0,l);//建树
for (int i = 1;i <= m;i ++)
{
cin >> ql >> qr;
cover (1,0,l,ql,qr);//砍树
}
cout << tree[1].cnt << endl;
//tree[1].cnt的值即为所有树的数量,输出即可
}
二、区间最值问题(RMQ)
- RMQ (Range Minimum/Maximum Query)
三、ST表
推荐博客:zzh的博客-浅谈ST表
(本文同步发表于洛谷博客 在luogu查看)
四、单点修改,区间查询
五、区间修改,区间查询
- lazytag 懒惰标记(OI Wiki 线段树(往下拉))
我们在区间修改时,我们可以先不修改,而是打一个标记,记录修改的值。
因为对子树内的每一个节点都修改的话,会浪费许多时间。因此,我们采用lazytag优化。