2010.4.10 第一周 线段树

一、建树

建树:
二分法
在这里插入图片描述

ll ls(ll p){ return p<<1 ; }
ll rs(ll p){ return p<<1|1 ; }
void push_up(ll p){ ans[p] = ans[ls(p)]+ans[rs(p)] ; }
void build(ll p, ll l , ll r)
{
	if(l == r)
	{
		ans[p] = a[l] ;
		return ;
	}
	ll mid = (l+r)>>1 ;
	build(ls(p),l,mid) ;
	build(rs(p),mid+1,r) ;
	push_up(p) ;
}

二、懒惰标记

可供区间修改与区间查询使用。
tag[]思路:先push_down往下传之前标记。若当前区间被完全覆盖,则做标记tag[],不继续往下递归,减少时间复杂度。注意tag不需回溯。
ans[]思路:建立时从上往下找到完全覆盖区间进行标记,再向上回溯(push_up)。

以下两函数用于懒惰标记。
f函数为操作。

void f(ll p , ll l , ll r ,ll k)
{
	ans[p] += (r-l+1)*k ;
	tag[p] += k ;
}

push_down作用为向下传一层标记,

void push_down(ll p, ll l , ll r)
{
	ll mid = (l+r)>>1 ;
	f(ls(p),l,mid,tag[p]) ;
	f(rs(p),mid+1,r,tag[p]) ;
	tag[p] = 0 ;
}

三、区间询问与区间修改

区间询问:
二分法思路:二分向下寻找到完全覆盖的当前区间。
若当前区间被完全覆盖,则返回res。

if(nl <= l && r <= nr)

否则继续向下判断查找。

if(nl <= mid)
if(mid < nr)

懒惰标记思路:
若未找到完全覆盖的区间,则往下传一层懒惰标记。即更新当前区间值,因此懒惰标记可以清零。
完整代码:

ll query(ll p , ll nl , ll nr , ll l , ll r)
{
	//cout << "query" << p <<" " << l <<" " << r << endl ;
	ll res = 0 ;
	if(nl <= l && r <= nr)
	{
		//cout << "ans[" << p << "]= " << ans[p] << endl ;
		return ans[p] ;
	}
	push_down(p,l,r) ;
	ll mid = (l+r)>>1 ;
	if(nl <= mid) res += query(ls(p) , nl , nr , l , mid ) ;
	if(mid < nr) res += query(rs(p) , nl ,nr , mid+1, r ) ;
	return res ;
}

区间修改:
区间修改:
二分思路:与查询相似。二分法从上往下找到完全覆盖的区间(即均要修改或查询的各区间)进行操作。
懒惰标记思路:每次向下一层先传当层懒惰标记,再进行update操作。

void update(ll p, ll nl, ll nr, ll l, ll r, ll k)
{
	if(nl <= l && r <= nr)//找到覆盖区间,进行操作 
	{
		f(p,l,r,k) ;
		return ; 
	}
	push_down(p,l,r) ;
	ll mid = (l+r)>>1 ;
	if(nl <= mid) update(ls(p) , nl , nr , l , mid , k ) ;
	if(mid < nr) update(rs(p) , nl ,nr , mid+1, r , k ) ;
	push_up(p) ;
}

单点修改:
二分查找到需要更改的点进行修改。
因为是从上往下进行修改,所以需要回溯。

push_up(p) ;

完整代码:

void update(ll p , ll l , ll r , ll x , ll k )//二分找点 
{
	if(l == r)
	{
		ans[p] += k ;
		return ;
	}
	ll mid = (l+r)>>1 ;
	if(x <= mid ) update(ls(p),l,mid,x,k) ;
	else update(rs(p),mid+1,r,x,k) ;
	push_up(p) ;
}

四、做的练习题

单点修改: A - 敌兵布阵 HDU - 1166
区间修改: C - A Simple Problem with Integers POJ - 3468

五、总结

1、学会用输出来代替debug,看整个动态过程,收获大。
2、写博客可以扣每个细节,发现很多之前以为没有问题的点(如建树)。
3、递推递归需要做下练习。
4、需要腾更多时间敲代码。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值