线段树入门

最近本菜鸡在学习线段树,于是想着学完写篇博客巩固一下。写的不好勿喷~

问题一:给出n个数,n<=100,和m个询问,每次询问区间[l,r]的和,并输出。

问题二:给出n个数,n<=100,和m个操作,每个操作可能有两种:1、在某个位置加上一个数;2、询问区间[l,r]的和,并输出。

问题三:给出n个数,n<=1000000,和m个操作,每个操作可能有两种:1、在某个位置加上一个数;2、询问区间[l,r]的和,并输出。

问题四:给出n个数,n<=1000000,和m个操作,每个操作修改一段连续区间[a,b]的值。

显然问题一问题二暴力枚举是可以的,但是对于三和四,你还想暴力枚举?回家洗洗睡吧

这个时候就需要一种强大的数据结构——线段树。

一、线段树基础

1.线段树是一颗二叉树,它储存的是区间的信息,每个节点以结构体的形式存储,通常结构体包括 左端点,右端点和要维护的信息等(比如区间和)。

2.基本思想——二分。

3.线段树的一般结构如下图

4.性质

(1)若一个节点对应的区间为[a,b],那么其左孩子区间为[a,(a+b)/2],有孩子区间为[(a+b)/2+1,b]

  (2)   对于节点k,左孩子节点为2*k,右孩子节点为2*k+1,这符合二叉树的性质

二、线段树的基本操作

结构体 维护的信息可适当根据题目的需要添加

struct node{
	int l,r,sum;// l r sum分别表示左端点,右端点,区间和
}tree[n<<2+1]; //大小记得开4倍

线段树的基本操作主要有五个

建树、单点查询、单点修改、区间查询、区间修改。

1.建树

对于二分到的每个节点,给它的左右孩子划分范围 如果碰到叶子节点,存储其信息,其次是状态合并 具体看代码理解

//建树
void build(int l,int r,int k){
	tree[k].l=l,tree[k].r=r;
	if(l==r){
		cin>>tree[k].sum;
		return ;
	}
	int mid=(l+r)>>1;
	build(1,mid,2*k);//递归左孩子
	build(mid+1,r,2*k+1);//递归右孩子
	tree[k].sum=tree[k*2].sum+tree[k*2+1].sum; //求和
}

2.单点查询

本质思想就是二分,判断区间所在位置即可,看代码理解

void query(int k){
	if(tree[k].l==tree[k].r){ //左右端点相等,是叶子节点,即找到了要查询的点
		ans=tree[k].sum;
		return ; //找到就返回
	}
	int mid=(tree[k].l+tree[k].r)>>1;
	if(x<=mid) query(k*2); //目标位置比中点靠左,递归左子树
	else query(2*k+1); //反之则递归右子树
}

3.单点修改

先找到位置再修改,记得修改与之相关的节点的区间和,具体看代码

void change(int k){
	if(tree[k].l=tree[k].r){ //找到
		tree[k].sum+=y;
		return ;
	}
	int mid=(tree[k].l+tre[k].r)>>1;
	if(x<=m) change(2*k);
	else change(2*k+1);
	tree[k].sum=tree[k*2+1].sum+tree[k*2].sum;//所有包含节点k的结点状态更新
}

 4.区间查询 查询区间【x,y】的和

mid=(l+r)/2

y<=mid ,即 查询区间全在,当前区间的左子区间,往左孩子走

x>mid 即 查询区间全在,当前区间的右子区间,往右孩子走

否则,两个子区间都走

void Sum(int k){
	if(tree[k].l>=x&&tree[k].r<=y){
		ans+=tree[k].sum;
		return ;
	}
	int m=(tree[k].l+tree[k].r)>>1;
	if(x<=m) Sum(k*2);
	else Sum(k*2+1);
}

5.区间修改

void add(int k)
{
    if(tree[k].l>=a&&tree[k].r<=b)
    {
        tree[k].w+=(tree[k].r-tree[k].l+1)*x;
        tree[k].f+=x;
        return;
    }
    if(tree[k].f) down(k);
    int m=(tree[k].l+tree[k].r)/2;
    if(a<=m) add(k*2);
    if(b>m) add(k*2+1);
    tree[k].w=tree[k*2].w+tree[k*2+1].w;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

只微

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值