【数据结构】线段树

24 篇文章 0 订阅
4 篇文章 0 订阅

线段树

1.线段树是什么

线段树顾名思义,它是一颗树,而且是一颗完全二叉树,它的每一个位置用来保存一条线段,故称为线段树。

2.线段树的作用

维护区间极值或和或乘积等

3.模板

先定义

#define ls x<<1   左儿子
#define rs ls|1   右儿子
#define LL ls,l,M
#define RR rs,M+1,r

3.1建树

if(l==r){f[x]=a[l];return;}
int mid=(l+r)>>1;
build(ls,l,mid);
build(rs,mid+1,r);
update(x);//f[x]=max(f[x],f[ls],f[rs]);
		//f[x]=f[ls]+f[rs];

3.2单点修改

if(l==r){
	f[x]=p;
	return;
}
int mid=(l+r)>>1;
if(k<=mid) find(ls,l,mid,l2,r2);
else find(rs,mid+1,r,l2,r2);

3.3查询

if(l==l2&&r==r2){
	ans+=f[x];//ans=max(ans,f[x]);
	return;
}
int mid=(l+r)>>1;
if(r2<=mid) find(ls,l,mid,l2,r2);
else if(l2>mid) find(rs,mid+1,r,l2,r2);
else{find(ls,l,mid,l2,mid);find(rs,mid+1,r,mid+1,r2);}

3.4区间修改

if(l==l2&&r==r2){
	bz[x]=1;
	修改f[x]的信息
	f[x]+=z/f[x]*=z...
}

int down(int x){
 	if(bz[x]){
 		修改f[ls]以及f[rs]的信息
 		bz[x]=0;
 		bz[ls]=bz[rs]=1;
		
		/或bz[ls]+=bz[x],bz[rs]+=bz[x],bz[x]=0;
		/或bz[ls]*=bz[x],bz[rs]*=bz[x],bz[x]=0;
 	}
}

3.5查找前驱

查找最前的一个满足f[x]>=val的位置
找到就退出

I sek(I l2,I r2,I val,I x=1,I l=1,I r=n){
	if(f[x].max<val||l>r2||r<l2) return 0;
	if(l==r) return l;
	I M=l+r>>1,t;
	return (t=sek(l2,r2,val,LL))?t:sek(l2,r2,val,opt,RR);
}

3.6查找后继

I sek(I l2,I r2,I val,I x=1,I l=1,I r=n){
	if(f[x].max<val||l>r2||r<l2) return 0;
	if(l==r) return l;
	I M=l+r>>1,t;
	return (t=sek(l2,r2,val,opt,RR))?t:sek(l2,r2,val,LL);
}

其实差别不大,打到一起就是这样。

I sek(I l2,I r2,I val,I op,I x=1,I l=1,I r=n){//op表示查找前驱还是后继
	if(f[x].max<val||l>r2||r<l2) return 0;
	if(l==r) return l;
	I M=l+r>>1,t;
	return (!op)?(t=sek(l2,r2,v,op,LL))?t:sek(l2,r2,v,op,RR)
	:(t=sek(l2,r2,v,op,RR))?t:sek(l2,r2,v,op,LL);
}

4.动态开点

将dg中用ls[]和rs[]来代替ls或rs
在建树或修改中判断
if(!ls[x]) ls[x]=++cnt;
dg(ls[x],l,mid);
if(!rs[x]) rs[x]=++cnt;
dg(rs[x],mid+1,r)

5.线段树合并

对于不同的题目线段树合并有不同的打法。
假设我们要将线段树Y合并到线段树X上。
1.可直接用线段树Y的每个节点更新线段树X上的节点(一般适用于区间极值)

int marge(int x,int y){
	if(!x||!y) return x+y;
	ls[x]=marge(ls[x],ls[y]);
	rs[x]=marge(rs[x],rs[y]);
	f[x]=max(f[x],f[y]);
}

2.用线段树Y的叶子更新线段树X的叶子,然后再用线段树X的左右儿子更新线段树X(一般用于区间和)

int marge(int x,int y,int l,int r){
	if(!x||!y) return x+y;
	if(l==r){
		f[x]+=f[y];
		return x;
	}
	int M=(l+r)>>1;
	ls[x]=marge(ls[x],ls[y],l,M);
	rs[x]=mge(rs[x],rs[y],M+1,r);
	update(x);
	return x;
}

练习(例题):

1959. 最大值
1960. 最大值2
3512. 【NOIP2013模拟11.5A组】游戏节目
5961. 【NOIP2018提高组D1T1】铺设道路
4231. 【五校联考4day1】寻找神格



作者:zsjzliziyang
QQ:1634151125
转载及修改请注明
本文地址:https://blog.csdn.net/zsjzliziyang/article/details/

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值