数据结构-线段树1(基于区间求和线段树)

🌳 线段树 学习笔记(一):基础模板与简单变式

一个算法,1题板子,1-2题上手,1-3题变式提升 ✅


📌 线段树是什么?

把数组不断二分,每个节点管一段区间,支持 O(log n) 的修改和查询。

线段树好用的一匹,只要是涉及区间求和(无论是正常运算还是二进制运算)、求区间最值求连续最大/最小子区间,都能包办。并且单点所有操作也能搞定,简直万能。

学会它只要记住四个步骤就行:

建树 → 改区间/点 → 查询区间 → 懒标记处理复杂修改
  ↓       ↓           ↓              ↓
build() update()   query()    lazy_tag & down() & up()

接下来带你一一回忆。


1. 宏定义 与 up() 死记硬背即可

只用传递 rtup() 只要进入了叶子节点修改了值都要加上。

#define ll long long
#define mid ((l + r) / 2)
#define lson (rt * 2)
#define rson (rt * 2 + 1)

void up(int rt)
{
    sum[rt] = sum[lson] + sum[rson];
}

2. build() 三段式:叶子 → 左右建树 → up()

// 建树:从原数组a构建线段树
// 表示正在构建[l,r]的树,此树sum节点为rt
void build(ll l, ll r, ll rt)
{
    if(l == r)                 // 来到叶子节点
    {
        sum[rt] = a[r];        // 叶子节点就是原数组
        return;
    }
    build(l, mid, lson);       // 建左子树
    build(mid + 1, r, rson);   // 建右子树

    up(rt);                    // 当前节点和 = 左右子树相加
}

3. down() 懒标记下传

先管 sonlazy,再管 sonsum,最后清空 lazy[rt]

它与懒标记 lazy_tag 同时出现,只有区间修改要用到懒标记时候才会出现

// 下传懒标记(将当前节点的懒标记传递给左右儿子)
void down(int rt, int l, int r)
{
    if(lazy[rt] == 0) return;   // [新增] 没标记直接返回

    lazy[lson] += lazy[rt];
    lazy[rson] += lazy[rt];

    sum[lson] += lazy[rt] * (mid - l + 1);
    sum[rson] += lazy[rt] * (r - mid);
    // 实际上是(r-(mid+1)+1)

    lazy[rt] = 0;              // 当前懒标记清零
}

4. update() 区间修改

如果包了就返回 → down() → 有左传左,有右传右 → up()

// 区间加:[L,R]统一+val
void update_add(ll L, ll R, ll val, ll l, ll r, ll rt)
{
    if(L <= l and r <= R)       // 如果当前区间已经被目标L,R包含
    {
        sum[rt] += (r - l + 1) * val;  // 区间和增加了:区间长度*val
        lazy[rt] += val;               // 懒标记累加
        return;                        // [新增] 注意是递归出口!
    }
    down(rt, l, r);            // 标记下传

    if(L <= mid) update_add(L, R, val, l, mid, lson);
    if(R > mid) update_add(L, R, val, mid + 1, r, rson);

    up(rt);
}

5. query() 区间查询

先判完全覆盖 → 直接返回;否则下传 → 递归左右,有左传左,有右传右 → 合并答案。

// 区间查询:查询[L,R]的区间和
// 在 [L,R] 中查询,当前子树为 [l,r],节点为 rt
ll query_sum(ll L, ll R, ll l, ll r, ll rt)
{
    if(L <= l and r <= R) return sum[rt];  // 完全覆盖,直接返回
    down(rt, l, r);                        // 下传懒标记

    ll sum = 0;
    if(L <= mid) sum += query_sum(L, R, l, mid, lson);
    if(R > mid) sum += query_sum(L, R, mid + 1, r, rson);

    return sum;
}

📋 已做题目回顾

题目 核心考点
P3372 【模板】线段树 1 区间加 + 区间求和(基础模板)
P1816 忠诚 只查询不修改,练习query
P1438 无聊的数列 差分 + 线段树维护等差数列
P3870 [TJOI2009] 开关 区间翻转 + 区间求和(懒标记存奇偶性)

💡 线段树核心四函数

void build(int l, int r, int rt)   // 建树
void up(int rt)                    // 向上合并
void down(int rt, int l, int r)    // 向下传懒标记
void update(int L, int R, int val, int l, int r, int rt)  // 区间修改
int query(int L, int R, int l, int r, int rt)             // 区间查询

口诀:建树先叶子,修改先判全盖,不全盖就下传,递归完就合并,查询同理。💪


附:完整代码

P3372 线段树1

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
typedef long long ll;

#define mid ((l+r)/2)
#define lson ((rt*2))
#define rson ((rt*2)+1)
#define int long long

ll n,m;
ll sum[N*4],lazy[N*4],a[N];

void build(int l,int r,int rt)
{
    if(l==r)
    {
        sum[rt]=a[r];
        return;
    }
    build(l,mid,lson);
    build(mid+1,r,rson);

    sum[rt]=sum[lson]+sum[rson];
}

void down(int l,int r,int rt)
{
    if(lazy[rt] == 0) return;   // [新增] 没标记直接返回

    lazy[lson]+=lazy[rt];
    lazy[rson]+=lazy[rt];

    sum[lson]+=lazy[rt]*(mid-l+1);
    sum[rson]+=lazy[rt]*(r-mid);

    lazy[rt]=0;
}

void update_add(int L,int R,int val,int l,int r,int rt)
{
    if(L<=l and r<=R)
    {
        sum[rt]+=(r-l+1)*val;
        lazy[rt]+=val;
        return;                 // [新增] 注意是递归出口!
    }
    down(l,r,rt);

    if(L<=mid) update_add(L,R,val,l,mid,lson);
    if(R>mid) update_add(L,R,val,mid+1,r,rson);

    sum[rt]=sum[lson]+sum[rson];
}

ll query_sum(int L,int R,int l,int r,int rt)
{
    if(L<=l and r<=R) return sum[rt];
    down(l,r,rt);

    ll sum=0;
    if(L<=mid) sum+=query_sum(L,R,l,mid,lson);
    if(R>mid) sum+=query_sum(L,R,mid+1,r,rson);

    return sum;
}

signed main()
{
    #ifndef ONLINE_JUDGE
        freopen("c.in","r",stdin);
        freopen("c.out","w",stdout);
    #endif

    cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>a[i];
    build(1,n,1);

    while(m--)
    {
        int op;
        cin>>op;
        if(op==1)
        {
            int x,y,k;
            cin>>x>>y>>k;
            update_add(x,y,k,1,n,1);
        }
        else if(op==2)
        {
            int x,y;
            cin>>x>>y;
            cout<<query_sum(x,y,1,n,1)<<endl;
        }
    }

    return 0;
}

P1816 忠诚

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
typedef long long ll;

#define int long long
#define mid ((l+r)/2)
#define lson ((rt*2))
#define rson ((rt*2)+1)

ll minv[N*4];
ll a[N];
int n,m;

void build(int l,int r,int rt)
{
    if(l==r)
    {
        minv[rt]=a[r];
        return;
    }

    build(l,mid,lson);
    build(mid+1,r,rson);

    minv[rt]=min(minv[lson],minv[rson]);
}

ll query(int L,int R,int l,int r,int rt)
{
    if(L<=l and r<=R) return minv[rt];

    ll minval=1e9;
    if(L<=mid) minval=min(minval,query(L,R,l,mid,lson));
    if(R>mid) minval=min(minval,query(L,R,mid+1,r,rson));

    return minval;
}

signed main()
{
    #ifndef ONLINE_JUDGE
        freopen("c.in","r",stdin);
        freopen("c.out","w",stdout);
    #endif

    cin>>m>>n;   // [新增] 注意输入顺序:m是长度,n是查询次数
    for(int i=1;i<=m;i++) cin>>a[i];

    build(1,m,1);

    while(n--)
    {
        int l,r;
        cin>>l>>r;
        cout<<query(l,r,1,m,1)<<' ';
    }

    return 0;
}

P1438 无聊的数列

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
typedef long long ll;

#define int long long
#define mid ((l+r)/2)
#define lson ((rt*2))
#define rson ((rt*2)+1)

ll a[N],cf[N],sum[N*4];
ll lazy[N*4];
ll n,m;

void build(int l,int r,int rt)
{
    if(l==r)
    {
        sum[rt]=cf[l];
        return;
    }

    build(l,mid,lson);
    build(mid+1,r,rson);

    sum[rt]=sum[lson]+sum[rson];
}

void down(int l,int r,int rt)
{
    if(lazy[rt] == 0) return;   // [新增] 没标记直接返回

    lazy[lson]+=lazy[rt];
    lazy[rson]+=lazy[rt];

    sum[lson]+=lazy[rt]*(mid-l+1);
    sum[rson]+=lazy[rt]*(r-mid);

    lazy[rt]=0;
}

void update(int L,int R,int val,int l,int r,int rt)
{
    if(L>R) return;              // [新增] 防止无效区间
    if(L<=l and r<=R)
    {
        sum[rt]+=val*(r-l+1);
        lazy[rt]+=val;
        return;
    }
    down(l,r,rt);

    if(L<=mid) update(L,R,val,l,mid,lson);
    if(R>mid) update(L,R,val,mid+1,r,rson);

    sum[rt]=sum[lson]+sum[rson];
}

ll query(int L,int R,int l,int r,int rt)
{
    if(L<=l and r<=R) return sum[rt];
    down(l,r,rt);

    ll sum=0;
    if(L<=mid) sum+=query(L,R,l,mid,lson);
    if(R>mid) sum+=query(L,R,mid+1,r,rson);

    return sum;
}

signed main()
{
    #ifndef ONLINE_JUDGE
        freopen("c.in","r",stdin);
        freopen("c.out","w",stdout);
    #endif

    cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>a[i],cf[i]=a[i]-a[i-1];   // [新增] 构造差分数组
    build(1,n,1);

    while(m--)
    {
        int opt;
        cin>>opt;
        if(opt==1)
        {
            int l,r,K,D;
            cin>>l>>r>>K>>D;
            update(l,l,K,1,n,1);                                 // [新增] 首项加K
            if(l+1<=r) update(l+1,r,D,1,n,1);                    // [新增] 公差加D
            if(r+1<=n) update(r+1,r+1,-(K+(r-l)*D),1,n,1);       // [新增] 差分末尾修正
        }
        else if(opt==2)
        {
            int p;
            cin>>p;
            cout<<query(1,p,1,n,1)<<endl;   // [新增] 单点查询 -> 差分前缀和
        }
    }

    return 0;
}

P3870 开关

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
typedef long long ll;

#define mid ((l+r)/2)
#define lson ((rt*2))
#define rson ((rt*2)+1)

int sum[N*4];
bool lazy[N*4];
int n,m;

void down(int l,int r,int rt)
{
    if(lazy[rt]==0) return;   // [新增] 没标记直接返回

    lazy[lson]^=lazy[rt];     // [新增] 翻转标记用异或
    lazy[rson]^=lazy[rt];

    sum[lson]=(mid-l+1)-sum[lson];   // [新增] 区间翻转:1的个数变成长度减原个数
    sum[rson]=(r-mid)-sum[rson];

    lazy[rt]=0;
}

void update(int L,int R,int l,int r,int rt)
{
    if(L<=l and r<=R)
    {
        sum[rt]=(r-l+1)-sum[rt];   // [新增] 翻转当前区间
        lazy[rt]^=1;               // [新增] 标记翻转奇偶
        return;
    }
    down(l,r,rt);

    if(L<=mid) update(L,R,l,mid,lson);
    if(R>mid) update(L,R,mid+1,r,rson);

    sum[rt]=sum[lson]+sum[rson];
}

int query(int L,int R,int l,int r,int rt)
{
    if(L<=l and r<=R) return sum[rt];
    down(l,r,rt);

    int sum=0;
    if(L<=mid) sum+=query(L,R,l,mid,lson);
    if(R>mid) sum+=query(L,R,mid+1,r,rson);

    return sum;
}

signed main()
{
    #ifndef ONLINE_JUDGE
        freopen("c.in","r",stdin);
        freopen("c.out","w",stdout);
    #endif

    int n,m;
    cin>>n>>m;

    while(m--)
    {
        int c,a,b;
        cin>>c>>a>>b;
        if(c==0) update(a,b,1,n,1);
        else if(c==1) cout<<query(a,b,1,n,1)<<endl;
    }

    return 0;
}

未完工,待补充,2026.8.12

posted @ 2026-08-12 16:24  敛才有道  阅读(2)  评论(0)    收藏  举报