要素:
线段树的定义一般用结构体定义
1 2 3 4
| struct Node{ int l,r; int }tr[N*4];
|
基本操作:
1.build(u , l , r)
建立一棵线段树
通用代码:
1 2 3 4 5 6 7 8 9 10
| void build(int u,int l,int r) { tr[u]={···}; if(l == r) return; int mid=(l+r)>>1; build(u<<1,l,mid); build(u<<1|1,mid+1,r); pushup(u); }
|
2.pushup(u)
通过左右子节点信息计算父节点信息
通用代码:
1 2 3 4 5
| void pushup(int u) { tr[u].v=function(tr[u<<1].v,tr[u<<1|1].v); }
|
3.pushdown(u)
:用父节点信息计算左右子节点的信息
懒标记:即为在修改时的修改值,用于对区间进行修改,使其用的时间复杂度进行区间修改,防止时间复杂度退化为
懒标记下传用
通用代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| void pushdown(int u) { auto &root=tr[u],&left=tr[u<<1],&right=tr[u<<1|1]; if(ro.flag) { left.flag->root.flag; right.flag->root.flag; { · · · · · · } root.flag=0; } }
|
4.query(u , l , r)
对区间进行查询操作(求和、求最大值等)
通用代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| int query(int u, int l, int r) { if (tr[u].l>=l&&tr[u].r<=r) return tr[u].v;
pushdown(u); int mid=tr[u].l+tr[u].r>>1; if(求和) { int sum=0; if(l<=mid) sum=query(u<<1,l,r); if(r>mid) sum+=query(u<<1|1,l,r); return sum; } if(求值) { if(l<=mid) return query(u<<1,l,r); if(r>mid) return query(u<<1|1,l,r); } }
|
5.modify
该操作包含两种操作:
1>单点修改modify(u , x , v):
:对单点进行修改(加/减一个数、改为一个数)
通用代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| void modify(int u,int x,int v) { if(tr[u].l==x&&tr[u].r==x) tr[u]={···};
else { int mid=tr[u].l+tr[u].r>>1;
if(x>mid) modify(u<<1|1,x,v); else modify(u<<1,x,v);
pushup(u); } }
|
2>区间修改modify(u , l , r , v):
:对区间进行修改(都加/减一个数、都改为一个数)
通用代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| void modify(int u,int l,int r,int d) { if(tr[u].l>=l&&tr[u].r<=r) { } else { pushdown(u); int mid=tr[u].l+tr[u].r>>1; if(l <= mid) modify(u<<1,l,r,d); if(mid<r) modify(u<<1|1,l,r,d); pushup(u); } }
|
习题:
单点修改:
P1198 最大数
AcWing.245你能回答这些问题吗
区间修改:
1>单懒标记:
AcWing 243. 一个简单的整数问题2
P3372 【模板】线段树 1
P1438 无聊的数列
2>双懒标记:
AcWing 1277. 维护序列
P3373 【模板】线段树 2