zoukankan      html  css  js  c++  java
  • 线段树学习笔记

    (Part 1:) 线段树简介

    线段树这个东西,学之前感觉很难很冷门,学之后感觉很难很热门。
    首先我们先理解一下“线段树”这个词语。顾名思义,根据我们语文老师提供的拆词法,拆成“线段”+“树”两个部分。易得,这是一种树形数据结构,其节点基于线段操作。
    我们使用一个插图来了解一下具体的一颗线段树:
    seg1.png
    显然可以看出,线段树的每个节点是一个线段。而在这个线段上,我们通常维护一些跟线段有关的值比如区间和或者最大值什么的。

    (1.Build Tree) 千里之行,始于建树

    这可不是什么好笑的事情,每年有那么多的(OIer)因为忘记建树而.......(缅怀同胞)
    如何建树呢?在建树的时候,我们通常把一些很基本的信息添加到树上去,一般是区间左右端点和区间和。
    如果当前节点是叶子节点,即(t[u].l==t[u].r),那么我们就把属于这一个长度为(1)的区间的值给到(t[u].sum),一般来说这个值题目会给出。
    如果不是叶子节点,那么我们先求出一个(mid),此处有二分的思想,向左右分别递归,形如:
    BuildTree(u*2,l,mid);
    BuildTree(u*2+1,mid+1,r);
    在递归完之后别忘了把左右儿子的值加给父节点:
    t[u].sum=t[u*2].sum+t[u*2+1].sum;

    (2.) 普天之下,莫非王土;率土之滨,区间修改

    区间修改是个重点。
    我们想一下,如果把区间修改改成(r-l+1)次单点修改,这个时间复杂度是我们不能接受的。
    区间修改的主要思想是#lazy-tag#,即,如果想要修改([1,6])这个区间如上图,那么我们并不是立即把修改操作向下传递到叶子节点,而是现在(1)号节点这里记录一个(tag)。具体的,如果我想([1,6]+=5),那么我先(t[1].tag+=5),其他地方先不改动。
    在修改完(tag)之后,需要立刻下传递一次,注意,是一次,所以时间复杂度是(Theta (1))。具体的操作如下:

    if(t[p].add)
    {
      	t[p*2].add+=t[p].add;
      	t[p*2+1].add+=t[p].add;
      	t[p*2].sum+=(t[p*2].r-t[p*2].l+1)*t[p].add;
      	t[p*2+1].sum+=(t[p*2+1].r-t[p*2+1].l+1)*t[p].add;
      	t[p].add=0;
    }
    

    这样做是为了之后从儿子节点更新(t[u].sum),具体操作见“千里之行”章节。
    之后也是一样,算出(mid)之后向左右递归即可。

    void pd(int p)
    {
    	if(t[p].add)
    	{
    		t[p*2].add+=t[p].add;
    		t[p*2+1].add+=t[p].add;
    		t[p*2].sum+=(t[p*2].r-t[p*2].l+1)*t[p].add;
    		t[p*2+1].sum+=(t[p*2+1].r-t[p*2+1].l+1)*t[p].add;
    		t[p].add=0;
    	}
    }
    void modify(int p,int l, int r,int d)
    {
    	if(l<=t[p].l&&t[p].r<=r){t[p].add+=d;t[p].sum+=(t[p].r-t[p].l+1)*d;return;}
    	pd(p);
    	int mid=(t[p].l+t[p].r)/2;
    	if(l<=mid)modify(p*2,l,r,d);
    	if(r>mid)modify(p*2+1,l,r,d);
    	t[p].sum=t[p*2].sum+t[p*2+1].sum;
    }
    

    (3.) 明察秋毫,区间查询

    区间查询其实跟区间修改特别像,详见“普天之下,莫非王土”章节。
    直接查找,向两侧递归即可。

    ll query(int p,int l,int r)
    {
    	if(l<=t[p].l&&t[p].r<=r)return t[p].sum;
    	pd(p);
    	int mid=(t[p].l+t[p].r)/2;
    	ll ans=0;
    	if(l<=mid)ans+=query(p*2,l,r);
    	if(r>mid)ans+=query(p*2+1,l,r);
    	return ans;
    }
    

    线段树的基本操作就讲到这里,看官们别忘了点赞收藏哦!

  • 相关阅读:
    hihocoder 1049 后序遍历
    hihocoder 1310 岛屿
    Leetcode 63. Unique Paths II
    Leetcode 62. Unique Paths
    Leetcode 70. Climbing Stairs
    poj 3544 Journey with Pigs
    Leetcode 338. Counting Bits
    Leetcode 136. Single Number
    Leetcode 342. Power of Four
    Leetcode 299. Bulls and Cows
  • 原文地址:https://www.cnblogs.com/juruo-wsy/p/14728124.html
Copyright © 2011-2022 走看看