线段树学习笔记

1 线段树初步

1.1 区间操作

1.1.1 已知的区间操作

  • 前缀和   —— 区间查询
  • 差分    —— 区间修改
  • ST表    —— 区间查询

没有可以同时进行修改和查询的!!!

1.1.2 能让ST表实现修改吗?

计算影响:

\(eg\).

  1. 修改 \(input[1]\)\(st[1][1]\) ~ \(st[1][log_2 n]\)              —— \(log_2 n\) 次修改
  2. 修改 \(input[2]\)\(st[1][2]\) ~ \(st[1][log_2 n]\)\(st[2][1]\) ~ \(st[2][log_2 n]\)  —— \(2 log_2 n\) 次修改
  3. 修改 \(input[n]\)\(st[1][log_2 n]\),……,\(st[n][1]\) ~ \(st[n][log_2 n]\)     —— \(n log_2 n\) 次修改

≈ 重写 ST表!!!

1.2 线段树理论

1.2.1 线段树的结构

SegTree

每个节点的特点:

  1. 每个节点是一个区间
  2. 每个节点存储 左端点,右端点,区间答案

1.2.2 线段树的存储

标号存储:

1.左子结点序号为 \(((id)<<1)\)

2.右子结点序号为 \(((id)<<1|1)\)

注意:线段树数组应该开 n*4

1.3 单修区查线段树的实现

例题 : Luogu: P3374 【模板】树状数组 1

注: 以下代码为此题的单点加数,区间求和为例

1.3.1 建树(build)

递归构建左右子树

//#define ls(id) ((id)<<1)
//#define rs(id) ((id)<<1|1)

void build(int id,int l,int r){
	if(l==r){
		tree[id]=input[l];	
    	return ;
    }
    int m=(l+r)>>1;
    build(ls(id),l,m);
    build(rs(id),m+1,r);
    // 标记
    return ;
}

标记处是不是少了什么?

我们只构建了叶子节点,其他节点都是空的!

设计一个 “上传更新” 函数

//#define ls(id) ((id)<<1)
//#define rs(id) ((id)<<1|1)

void pushup(int id){
    tree[id]=tree[ls(id)]+tree[rs(id)];
    return ;
}

void build(int id,int l,int r){
	if(l==r){
		tree[id]=input[l];	
    	return ;
    }
    int m=(l+r)>>1;
    build(ls(id),l,m);
    build(rs(id),m+1,r);
    pushup(id);
    return ;
}

1.3.2 修改(update)

递归寻找需要修改的节点

//#define ls(id) ((id)<<1)
//#define rs(id) ((id)<<1|1)

void pushup(int id){
    tree[id]=tree[ls(id)]+tree[rs(id)];
    return ;
}

void update(int id,int l,int r,int q,int v){
	if(l==r){
		tree[id]+=v;	
    	return ;
    }
    int m=(l+r)>>1;
    if(q<=m) update(ls(id),l,m,q,v);
    else update(rs(id),m+1,r,q,v);
    pushup(id);
    return ;
}

1.3.3 查询(query)

当询问区间完全包含当前区间时,返回当前区间答案

//#define ls(id) ((id)<<1)
//#define rs(id) ((id)<<1|1)

void pushup(int id){
    tree[id]=tree[ls(id)]+tree[rs(id)];
    return ;
}

int query(int id,int l,int r,int ql,int qr){
	if(ql<=l&&r<=qr){	
    	return tree[id];
    }
    int m=(l+r)>>1,ans=0;
    if(ql<=m) ans+=query(ls(id),l,m,ql,qr);
    if(qr>m) ans+=query(rs(id),m+1,r,ql,qr);
    return ans;
}

1.3.4 完整 AC 代码

点击查看代码
#include<bits/stdc++.h>
#define ls(id) (id<<1)
#define rs(id) (id<<1|1)
using namespace std;

const int maxn=1e6+5;
int tree[maxn*4],input[maxn];

void pushup(int id){
	tree[id]=tree[ls(id)]+tree[rs(id)];
	return ;
}

void build(int id,int l,int r){
	if(l==r){
		tree[id]=input[l];	
    	return ;
    }
    int m=(l+r)>>1;
    build(ls(id),l,m);
    build(rs(id),m+1,r);
    pushup(id);
    return ;
}

void update(int id,int l,int r,int q,int v){
	if(l==r){
		tree[id]+=v;	
    	return ;
    }
    int m=(l+r)>>1;
    if(q<=m) update(ls(id),l,m,q,v);
    else update(rs(id),m+1,r,q,v);
    pushup(id);
    return ;
}

int query(int id,int l,int r,int ql,int qr){
	if(ql<=l&&r<=qr){	
    	return tree[id];
    }
    int m=(l+r)>>1,ans=0;
    if(ql<=m) ans+=query(ls(id),l,m,ql,qr);
    if(qr>m) ans+=query(rs(id),m+1,r,ql,qr);
    return ans;
}

int main(){
	
	int n,q;
	cin>>n>>q;
	for(int i=1;i<=n;i++){
		cin>>input[i];
	}
	build(1,1,n);
	for(int i=1;i<=q;i++){
		int type,x,y;
		cin>>type>>x>>y;
		if(type==1)
            update(1,1,n,x,y);
		else
            cout<<query(1,1,n,x,y)<<"\n";
	}
	return 0;
}
posted @ 2025-12-06 17:35  Sail-With-Dawn  阅读(10)  评论(0)    收藏  举报