线段树

线段树

引入

线段树是算法竞赛中常用的用来维护 区间信息 的数据结构。

线段树可以在 O(log N)的时间复杂度内实现单点修改、区间修改、区间查询(区间求和,求区间最大值,求区间最小值)等操作。 ——oi wiki

基本结构

  • 线段树的结构本质上是一个二叉树。

  • 每个点记录其所维护的区间值图片

  • 设一父节点编号为 \(p\),那么其左子节点为 \(p\times2\),右子节点为 \(p\times 2+1\)

建树

变量声明:

struct Tree{
	ll sum;
	ll add;
	int l, r;
}tree[500000];

ll a[100005];

递归建树:

void build(int p, int l, int r){
	tree[p].l = l;
	tree[p].r = r;
	tree[p].add=0;
	if(l==r){
		tree[p].sum=a[l]; 
		return; 
	}
	
	int mid=(l+r)>>1;
	build(p*2, l, mid);
	build(p*2+1, mid+1, r);
	tree[p].sum = tree[p*2].sum + tree[p*2+1].sum;
}

懒标记

懒标记的作用是记录每次、每个节点要更新的值,也就是 Δ。但线段树的优点不在于全记录,而在于传递式记录。

流程为:

  • 更新子节点的lazytag
  • 更新当前节点的结果
  • 将当前节点的tag初始化
void pushDown(int p){
	if(tree[p].add){
        //维护两个子节点的lazytag
		tree[p*2].add += tree[p].add;
        tree[p*2+1].add += tree[p].add;
		//利用父节点的tag维护两个子节点的结果
		tree[p*2].sum += tree[p].add*(tree[p*2].r-tree[p*2].l+1);
		tree[p*2+1].sum += tree[p].add*(tree[p*2+1].r-tree[p*2+1].l+1);
        //将父节点的lazytag初始化
		tree[p].add = 0;
	}
}

更新流程

(以区间加一个数 k 为例)

  1. 检查当前节点是否完全包含在目标区间内
    • 如果当前节点的区间 [tree[p].l, tree[p].r] 完全包含在目标区间 [l, r] 内,则直接更新当前节点的值:
      • 更新 sumsum += k * (区间长度)
      • 更新 add 标记:add += k
    • 然后返回,不需要继续递归。
  2. 如果当前节点不完全包含在目标区间内
    • 调用 pushdown,将当前节点的懒标记下传给子节点。
    • 递归更新左子节点和右子节点。
    • 递归结束后更新当前节点的 sum 值。
void update(int p, int x, int y, ll k){
    int l=tree[p].l, r=tree[p].r;
    if(y<l || x>r) return; //检查越界
	if(x<=l && y>=r){
        //当前节点被包含在目标区域内
        //同时也是这一条递推线上的lazytag推到头了,做出与pushdown类似的操作
		tree[p].sum += k * (r-l+1);
		tree[p].add += k;
        //更新完结果和标记后返回
		return;
	}
    //对于大于目标区域的节点:1.下推标记;2.向下递归;
	pushDown(p); 

	update(p*2, x, y, k);
	update(p*2+1, x, y, k) ;
    //返回后向上更新结果
	tree[p].sum = tree[p*2].sum + tree[p*2+1].sum;
}

查询流程

线段树的查询操作是递归地查找与目标区间有交集的节点,并合并结果。

对于查询区间 [L, R]

  • 从根节点开始,检查当前节点表示的区间 [left, right] 是否与 [L, R] 有交集。

  • 如果没有交集,直接返回无效值(例如,查询区间和时返回 0)。

  • 如果当前节点区间完全包含在 [L, R] 内,直接返回当前节点的值。

  • 否则,递归查询左右子节点,并合并结果(例如,查询区间和时返回左右子节点的和)。

ll request(int p, int x, int y){
	ll ans=0;
    int l=tree[p].l, r=tree[p].r;
    if(y<l || x>r) return 0;
	if(x<=l && y>=r){
		return tree[p].sum;
	}
	pushDown(p);

	ans += request(p*2, x, y);
	ans += request(p*2+1, x, y);
	return ans;
}
posted @ 2025-02-18 23:09  yangzwww  阅读(39)  评论(0)    收藏  举报