◎引例◎

在详细解释之前,我们先来看一下洛谷上的两个线段树模板题目(洛谷P3372P3373)。

从这几到题目可以看出,线段树的基本操作大致有三种:①给区间中的每一个元素加一个值  ②给区间中的每一个元素乘一个值  ③求出一个区间的每个元素和,下面我们先对线段树做一个基本的了解。

 ◎线段树的定义和结构◎

线段树的定义

       线段树是一种二叉搜索树,时间复杂度为O(nlogn)。

线段树的结构

       线段树,顾名思义,是树形结构,如下图所示:

       线段树应用了二分的思想,“树根”带表总的区间,然后不断对区间二分,直到代表单个值。在上述两个例题中,线段树的节点值都代表了所覆盖区间的元素和,在一些其他情况下,这个节点值也可以代表其他的东西,例如:区间的最大值或者最小值等等。

◎线段树的建树◎

       线段树的建树过程就是一个不断二分区间的过程(解释的通俗,但是可能不准确……)。从最大的区间开始,一步一步的二分,直到左右区间重合,也就是到了叶子结点,就开始给线段树赋值,下面附建树代码(本文的代码都是上文例题中的节选,在不同的题目中,可能有所不同)

 

void build(int l,int r,int u){ //[l,r]区间,u节点 
    tree[u].l=l,tree[u].r=r;
    if(tree[u].l==tree[u].r){ //[l,r]区间只有一个数(到达低端) 
        scanf("%lld",&tree[u].w);
        return;
    }
    int mid=(l+r)>>1;
    build(l,mid,u<<1); //建立左子树 
    build(mid+1,r,u<<1|1); //建立右子树 
    tree[u].w=tree[u+u].w+tree[u+u+1].w; //根节点=左子树+右子树
}

 

 

 

◎线段树的区间修改◎

        区间修改可以说是线段树最难理解的一部分,因为在这里有线段树特有的标记,名叫——懒惰标记(lazytag)。这个标记的大致作用就是在修改的时候,只修改当前节点,而不是全部,这也是线段树时间复杂度低的原因,而这就用到了lazytag,用来记录修改操作。然后,下一次修改时,如果当前的标记影响到了修改,就将标记下推。

◎线段树的区间查询◎

       在熟练区间修改之后,查询就相对来说简单了,依旧是应用了二分的思想。

◎例题代码◎

例题(一)

#include<bits/stdc++.h>
#define MAXN 100007
using namespace std; int n,m,x,y,k,q; long long ans; struct Tree{ int l; int r; //[l,r]区间 
    long long w; //当前区间的值 
    int lazytag; }tree[MAXN<<2]; //MAXN*2^2
inline void build(int l,int r,int u){ //[l,r]区间,u节点 
    tree[u].l=l,tree[u].r=r; if(tree[u].l==tree[u].r){ //[l,r]区间只有一个数(到达低端) 
        scanf("%lld",&tree[u].w); return; } int mid=(l+r)>>1; build(l,mid,u+u); //建立左子树 
    build(mid+1,r,u+u+1); //建立右子树 
    tree[u].w=tree[u+u].w+tree[u+u+1].w; //根节点=左子树+右子树
} inline void down(int u){ tree[u+u].lazytag+=tree[u].lazytag; tree[u+u+1].lazytag+=tree[u].lazytag; //lazytag下推 
    tree[u+u].w+=(tree[u+u].r-tree[u+u].l+1)*tree[u].lazytag; tree[u+u+1].w+=(tree[u+u+1].r-tree[u+u+1].l+1)*tree[u].lazytag; //重置区间值
    tree[u].lazytag=0; //删除lazytag
} inline void change(int u){ if(tree[u].l>=x && tree[u].r<=y){ //--->[x,y]区间包含[l,r]区间
        tree[u].w+=(tree[u].r-tree[u].l+1)*k; //[l,r]的和加个数*k
        tree[u].lazytag+=k; //lazytag----该节点以下的叶节点+k
        return; } if(tree[u].lazytag) down(u); //有lazytag就下推
    int mid=(tree[u].l+tree[u].r)>>1; if(x<=mid) change(u+u); //修改左区间
    if(mid<y) change(u+u+1); //修改右区间
    tree[u].w=tree[u+u].w+tree[u+u+1].w; //重置节点值
} inline void ask(int u){ if(tree[u].l>=x && tree[u].r<=y){ //--->[x,y]区间包含[l,r]区间
        ans+=tree[u].w; return; } if(tree[u].lazytag) down(u); int mid=(tree[u].l+tree[u].r)>>1; if(x<=mid) ask(u+u); if(y>mid) ask(u+u+1); } int main(){ scanf("%d%d",&n,&m); build(1,n,1); for(int i=1;i<=m;i++){ scanf("%d",&q); if(q==1){ scanf("%d%d%d",&x,&y,&k); change(1); } else{ ans=0; scanf("%d%d",&x,&y); ask(1); printf("%lld\n",ans); } } return 0; }

 

例题(二)

 

#include <iostream>
#include <cstdio>
#include <cctype>

#define R register int
#define ls u << 1
#define rs u << 1 | 1
#define mid ((t[u].l + t[u].r) >> 1)
#define zq l, mid
#define yq mid + 1, r
#define len(L) t[(L)].r - t[(L)].l + 1
const int M = 500500;
typedef long long ll;
ll n, m, p, ans;
ll opt, x, y, k;
struct Tree{
    int l, r;
    ll w, ad, mu;
    Tree(){
        l = r = w = ad = 0;
        mu = 1;
    }
    void clear(){
        ad = 0;
        mu = 1;
    }
    void color(ll add, ll mul)
    {
        ad = (ad * mul + add) % p;
        mu = mu * mul % p;
        w = (w * mul + (r - l + 1) * add) % p;
    }
}t[M << 1];


inline void read(ll &now)
{
    now = 0;
    char c = getchar();
    while (!isdigit(c)) c = getchar();
    while (isdigit(c))
    {
        now = now * 10 + (c ^ 48);
        c = getchar();
    }
}

inline void down(int u)
{
    t[ls].color(t[u].ad, t[u].mu);
    t[rs].color(t[u].ad, t[u].mu);
    t[u].clear();
}

inline void update(int u) { t[u].w = (t[ls].w + t[rs].w) % p; }

void build(int l, int r, int u)
{
    t[u].l = l, t[u].r = r;
    if (l == r)
    {
        read(t[u].w);
        t[u].w %= p;
        return;
    }
    build(zq, ls);
    build(yq, rs);
    update(u);
}

void change(int u, int mul, int add)
{
    if (t[u].l >= x && t[u].r <= y)
    {
        t[u].color(add, mul);
        return;
    }
    if (t[u].ad || t[u].mu != 1) down(u);
    if (mid >= x) change(ls, mul, add);
    if (mid < y) change(rs, mul, add);
    update(u);
}

void query(int u)
{
    if (t[u].l >= x && t[u].r <= y)
    {
        ans = (ans + t[u].w) % p;
        return;
    }
    if (t[u].ad || t[u].mu != 1) down(u);
    if (mid >= x) query(ls);
    if (mid < y) query(rs);
}

int main()
{
    read(n), read(m), read(p);
    build(1, n, 1);
    for (R i = 1; i <= m; ++i)
    {
        read(opt);
        if (opt == 1)
        {
            read(x), read(y), read(k);
            change(1, k, 0);
        }
        else if (opt == 2)
        {
            read(x), read(y), read(k);
            change(1, 1, k);
        }
        else
        {
            read(x), read(y);
            ans = 0;
            query(1);
            printf("%lld\n", ans);
        }
    }
    return 0;
}