线段树详解(包含加法线段树、乘法线段树及区间根号线段树,简单易懂)

同步发表于 优快云@return_dr

这一篇文章我们将对线段树中的常规操作进行详细的讨论。

  • 以下所提到的复杂度如无特殊说明均为时间复杂度。 log ⁡ \log log 的底数均为 2 2 2

不开 long long 见祖宗!

第一部 普通线段树

一、引入

对于一串数,举例为 a a a,需要实现以下几种操作(修改操作均为加减法):

  1. 单点修改
  2. 单点查询
  3. 区间修改
  4. 区间查询(区间和)

显然 1 1 1 2 2 2 3 3 3 4 4 4 包含。但为了讨论的清晰,我们也将对其单独讨论。

如果我们用常规方式(数组)存储这串数,其查询操作为 O ( 1 ) O(1) O(1),更改操作为 O ( 1 ) O(1) O(1),求和操作为 O ( l ) O(l) O(l) l l l 为所求序列长度)。

#define LL long long // 不开 long long 见祖宗!
#define ref(i, a, b, p) for (signed(i) = (a); (i) <= signed(b); (i) += signed(p))
const int maxn = 50005;
LL a[maxn], n, q, sum;
void work()
{
   
    cin >> n;
    ref (i, 1, n, 1)
        cin >> a[i];
    cin >> q;
    ref (__, 1, q, 1)
    {
   
        int x, k, l, r;
        cin >> x;
        if (x == 1) // 以下均如题
        {
   
            cin >> k;
            a[x] = k;
        }
        else if (x == 2)
            cout << a[x] << endl;
        else if (x == 3)
        {
   
            cin >> l >> r >> k;
            ref (i, l, r, 1)
                a[x] += k;
        }
        else
        {
   
            sum = 0;
            cin >> l >> r;
            ref (i, l, r, 1)
                sum += a[i];
            cout << sum << endl;
        }
    }
}

如果用前缀和来操作的话,虽然求和操作为 O ( 1 ) O(1) O(1),但是更改数值后更新前缀和的复杂度为 O ( n ) O(n) O(n),没有起到太大的优化效果,不再放代码。

有人说 O ( n ) O(n) O(n) 的时间复杂度已经很优了,但是设想,如果有 q q q 次询问,那么总的复杂度为 O ( n q ) O(nq) O(nq),不可接受。

二、优化方案

经过前面优先队列等数据结构的铺垫,我们知道,是一种能在复杂度为 O ( log ⁡ n ) O(\log n) O(logn) 的情况下处理大部分操作的数据结构。

那么我们可以这样想:对于一串数 a a a,我们把它看成一条长度为 n n n 的线段,标记线段左端点为 1 1 1,右端点为 n n n,那么我们让线段上的 1 1 1 对应 a 1 a_1 a1,让 2 2 2 对应 a 2 a_2 a2……让 n n n 对应 a n . a_n. an.,这样我们就用一条线段表示出了 a a a

我们此时建立一棵完全二叉树(偶数个数的情况下为满二叉树),以刚刚建立的线段上的每一个点(从 a 1 a_1 a1 a n a_n an)为叶子节点,用父节点表示其左右儿子的和,那么根节点就为整条线段(也就是整串数)的和。

具体来看(代码放到最后)
建树时,先建立叶子节点,然后逐层回溯计算父结点的值,每个节点需要存储的变量有它的值以及左右端点(就是区间范围)。时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)

如下图,先建立节点 1 , 2 , 3 … 8 1,2,3\dots8 1,2,38(代表 a 1 , a 2 , a 3 … a 8 a_1,a_2,a_3\dots a_8 a1,a2,a3a8,下同),然后逐层回溯更新节点 a , b , c … g a,b,c\dots g a,b,cg,更改顺序:红、黄、绿、蓝。
在此图中:

a = 1 + 2 ,   b = 3 + 4 ,   c = 5 + 6 ,   d = 7 + 8. a=1+2,\,b=3+4,\,c=5+6,\,d=7+8. a=1+2,b=3+4,c=5+6,d=7+8.

e = a + b = ∑ i = 1 4 a i ,   f = c + d = ∑ i = 5 8 a i . e=a+b=\sum\limits_{i=1}^{4}a_i,\,f=c+d=\sum\limits_{i=5}^{8}a_i. e=a+b=i=14ai,f=c+d=i=58

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值