线段树题型总结(更新中)

线段树用来降复杂度,到O(nlogn),往往涉及的关键词:区间,实时更新。

#define ls		i<<1
#define rs		i<<1|1
#define mid     (l+r>>1)
#define lson	ls,l,mid
#define rson	rs,mid+1,r
#define ln		(mid-l+1)
#define rn		(r-mid)
//上面是常用的,并且ln,rn得加括号
#define ison    i,l,r		//吐槽:这个是我自创的,有点歧义,还是一般用i,l,r写吧

int m=l+r>>1
数组开4倍空间    <<2

有些写法中,pushup和pushdown函数都只有两个变量
void pushup(int i,int m)	--->pushup(i,r-l+1)
void pushdown(int i,int m)	--->pushdown(i,r-l+1)
这时
(m-(m>>1))==m-l+1==ln
(m>>1)==r-m==rn

区间覆盖

母题:POJ2528

区间覆盖本质还是区间修改,修改成同一个值,只不过区间覆盖下,普通的离散化会有缺陷。上面博客中有样例。

这个点本身不难,但这种点很好的体现了延迟标记的作用,而这正是线段树节省时间的关键点之一,涉及到这个区间时才用延迟标记下推下去。要考虑pushdown函数(下推)什么时候可以不用加,所以所有操作下来,不是所有的下推函数推到底的。

单单区间覆盖不用上推,但不意味着涉及区间覆盖不用上推pushup函数、


区间合并

数组内的各个值往往含有某种关系式,最后问一个区间或这一点包含的区间的关系式的值,同时含有单点或区间修改操作。

代码思路:变量要设三个,其他操作类似

int lc[maxn<<2],rc[maxn<<2],num[maxn<<2];
lc		(最)左区间长度
rc		(最)右区间长度
num		当前区间最大长度
rc[ls]+lc[rs]	中间长度(前提是符合 合并条件)
区间合并+单点修改操作环境下
//左区间,右区间,当前区间的关系式值
int lc[maxn<<2],rc[maxn<<2],num[maxn<<2];
//上推操作视题意而定
void pushup(int i,int l,int r){
	//正常情况下的区间上推
	lc[i]=lc[ls];	lc[i]=rc[rs];
	num[i]=max(num[ls],num[rs]);
	//当中间符合 合并条件时
	int m=l+r>>1;
	//下面再次更新lc[i]、lc[i]、num[i]
	......
}
//建树操作一般相同
void build(int i,int l,int r){
	if(l==r){
		lc[i]=rc[i]=num[i]=1;
		return;
	}
	int m=l+r>>1;
	build(lson);	build(rson);
	pushup(i,l,r);
}
//单点修改a[k]=x时
void update(int i,int l,int r,int k,int x){	
	if(l==r&&l==k){
		.....
		return;
	}
	int m=l+r>>1;
	if(k<=m)	update(lson,k,x);
	else		update(rson,k,x);
	pushup(i,l,r);
}
//区间查询
int query(int i,int l,int r,int x,int y){
	if(x<=l&&r<=y)	return num[i];
	int m=l+r>>1;
	int temp=0;
	....
	return temp;
}

区间合并+区间修改操作环境下
void pushup(int i,int l,int r){
	//正常情况下的区间上推
	lc[i]=lc[ls];	lc[i]=rc[rs];
	num[i]=max(num[ls],num[rs]);
	//当中间符合 合并条件时
	int m=l+r>>1;
	//下面再次更新lc[i]、lc[i]、num[i]
	......
}

void pushdown(int i,int l,int r){
	if(f[i]!=-1){
		.....
		f[i]=-1;	//标记重置
	}
}

void build(int i,int l,int r){
	//lc[i],rc[i],num[i]修改
	....
	f[i]=-1;
	if(l==r)	return;
	int m=l+r>>1;
	build(lson);	build(rson);
}

void update(int i,int l,int r,int x,int y,int c){
	if(x<=l&&r<=y){
		//lc[i],rc[i],num[i]修改
		....
		f[i]=c;
		return;
	}
	pushdown(i,l,r);
	int m=l+r>>1;
	if(x<=m)	update(lson,x,y,c);
	if(y>m)		update(rson,x,y,c);
	pushup(i,l,r);
}
int query(int i,int l,int r,int x){
	int m=l+r>>1;
	pushdown(i,l,r);
	//下面视题意而定
	....
}

母题:HDU 1540(单点修改环境下)
练习题:
1、HDU3308 LCIS(LCIS+单点修改环境下)

2、POJ-3667Hotel(区间修改环境下母题)

3、


扫描线

待完善


可持久化线段树

入门:求静态第k小

源于线段树,还加了前缀和的思想,并且用公共的节点优化。一般静态开20倍,也可以<<5.

设原序列有n个数,含有m次询问
空间复杂度:(建空树)4*n+(前缀和更新)nlog2n
一般我们数组大小就开nlog2n (动态不一样)
时间复杂度:mlog2n


其他

HDU-4614 线段树+二分

2020CCPC威海 Caesar Cipher线段树+哈希

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值