线段树_例题(HDU1166—敌兵布阵+HDU1754—I Hate It )+板子

又是一个学习周期,今天学了线段树基础版的,早上困得一批,什么也没听进去,只能下午自己来脑补了

首先说什么是线段树,这有一篇大佬的blog  https://blog.csdn.net/zearot/article/details/4829945线段树详解

自我理解就是一个进阶版的分治过程,先把整个大区间拆分开来,弄成好过好多个小区间(当然拆到最后还剩一个节点)通过对小区间操作然后修改与他相连的节点从而改变大区间

这样的操作只处理了整个区间的一部分,并不需要去遍历整个区间,大大节省了时间开销

据说现在的基础版本比较模板化,现在我就先奉上这两个题的代码模板吧

题目链接:hdu1166 敌兵布阵

代码:         点击即得hdu1166的AC代码(线段树)

今天学了树状数组,发现敌兵布阵这道题用树状数组来做会更简单

代码:         这是HDU1166 敌兵布阵的AC代码(树状数组写的)

题目链接:hdu1754 I Hate It

代码:         这里是HDU1754 的AC代码

 

下面是线段树的小板板,当然板子仅供参考,并不是万能的,深入理解后适当修改即可拿去AC题目

/*储存方式
通常用的都是堆式储存法,即编号为k的节点的左儿子编号为k*2k*2k*2,右儿子编号为k*2+1,用位
运算优化一下,以上的节点编号就变成了k>>1,k>>1l1。其它储存方式请见指针储存和动态开点。通常,
每一个线段树上的节点储存的都是这几个变量:区间左边界,区间右边界,区间的答案(这里为区间
元素之和)*/
struct node{
	int l,r,sum,max,lazy;
}a[N]//N为总节点数

// 更新节点k的信息
void update(int k){ 
	a[k].sum=a[k<<1].sum+a[k<<1|1].sum;
	//- 段区间的元素和等于它的子区间的元素和
	a[k].max=max(a[k<<1].sum,a[1<<1|1].sum);
	//- 段区间内的最大值等于左区间的最大值和右区间最大值的最大值
}

//初始化==建树
void build(int k/*当前节点的编号*/,int l/*当前区间的左边界*/,int r/*当前区间的右边界*/){
	a[k].l=l,a[k].r=r;
	if(l==r){//递归到叶节点
		a[k].max=a[k].sum=number[l]://其中number数组为给定的初值
		return;
	}
	int mid=(l+r)>>1;//计算左右子节点的边界
	build(k<<1,l,mid);//递归到左儿子
	build(k<<1|1,mid+1,r);//递归到右儿子
	update(k);//记得要用左右子区间的值更新该区间的值
}

//单点修改
void change(int k/*当前节点的编号*/.int x/*要修改节点的编号*/,int y/*要把编号为x的数字修改成y*/)
	if(a[k].l==a[k].r){//如果当前区间只包含一个元素,那么该元素一定就是我们要修改的。
		a[k].max=a[k].sum=y;//由于该区间的sum一定等于编号为x的数字,所以直接修改sum就可以了。
		return;
	}
	//pushdown(k);
	int mid=(a[k].l+a[k].r)/2;//计算下一层子区间的左右边界
	if(x<=mid) 
		change(k<<1,x,y);//递归到左儿子
		else change(k<<1|1,x,y);//递归到右儿子
	update(k);//记得更新点k的值
}

//区间修改
void changeSegment(int k,int l,int r,int x){
//当前到了编号为k的节点,要把[l,r]区间中的所有元素的值+x
	if(a[k].>=l&&a[k].r<=r){//如果找到了全部元素都要被修改的区间 
		a[k].sum=(a[k].r-a[k].l+1)*X;//更新该区间的sum
		a[k].max=x;//更新该区间的max
		a[k].lazy=x;//懒惰标记叠加 
		return;
	}
	pushdown(k);
	int mid=(a[k].l+a[k].r)>>1;
	if(r>mid) changeSegment(k<<1|1,r,x); 
	if(l<=mid) changeSegment(k<<1,r,x);
	update(k);
	//记得更新点k的值
}

//下传标记
void pushdown(int k){//将点k的懒惰标记下传
	if(a[k].lazy==0)
		return ;//如果节点k已经是叶节点了,没有子节点,那么标记就不用下传,直接删除就可以了
	a[k<<1].sum=(a[k<<1].r-a[k<<1].1+1)*a[k].lazy;
	a[k<<1l1].sum=(a[k<<1|1].r-a[k<<1|1].l+1)*a[k].lazy;
	a[k<<1].max= a[k<<1|1].max=a[k].lazy;//给k的子节点重新赋值
	a[k<<1]lazy=a[k<<1|1].lazy=a[k].lazy;//下传点k的标记
	a[k].lazy=0;//记得清空点k的标记
}

//单点查询 
int findmax(int k){
	if(a[k].l==a[k].r)
		return a[k].max;
	pushdown(k);
	if(a[k<<1].max>a[k<<1|1].max)
		return findmax(k<<1);
	else
		return findmax(k<<1|1);
}

//区间求和 
int query(int k,int l,int r){//当前到了编号为k的节点,查询[l..r]的和 
	pushdown(k);
	//如果当前节点被打上了懒惰标记,那么就把这个标记下传,这一句其实也可以放在下一语句的后面
	if(a[k].l>=l&&a[k].r<=r) 
		return a[k].sum;
	int mid=(a[k].l+a[k].r)>>1;
	int x=0; 
	if(r>mid) 
		x+=query(k<<1|1,1,r);
	if(<=mid) 
		x+=query(k<<1,l,r);
	return x;
}

//区间查询最大值  和上一个函数类似 
int query(int k,int l,int r){
	pushdown(k);
	if(a[k].l>=l&&a[k].r<=r)
		return a[k].max;
	int mid=a[k].l+a[k].r>>1;
	int maxt=-1;
	if(r>mid)maxt=max(maxt,query(k<<1|1,l,r));
	if(l<=mid)maxt=max(maxt,query(k<<1,l,r));
	return maxt;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值