线段树(1):点修改

动态范围最小值问题。给出一个有n个元素的数组a[1],a[2],a[3]...a[n],我们的任务是设计一个数据结构,支持以下两种操作。

1,Update(x,v):把a[x]修改为v。

2,Query(L,R):计算min{a[L],a[L+1],a[L+2],...a[R]}

如果还是使用Sparse-Table算法,每次update操作都需要重新计算d数组,时间上无法承受。为了解决这个问题,这里介绍一种灵活的数据结构:线段树(segment tree)。

如下图,线段[1,8]所对应的线段树。每个非叶子结点都有左右两颗子树,分别对应该线段的左半和右半。为了方便,我们按照从上到下,从左到右的顺序给所有的结点编号为1,2,3...。则结点编号为i的结点,起左右子结点编号分别为2i和2i+1。


在不同的题目中,线段可以有不同的含义,如数轴上的一条真实的线段,或者一个序列中的连续子序列。在动态最小值问题中,用线段[L,R]代表子序列a[L],a[L+1],a[L+2]....,a[R]。上图只是展示了线段树中各个结点所对应的的线段,但对于用到线段树的大部分题目来说,这些线段所拥有的附加信息才是重头戏。本题需要维护最小值信息,因此可以在每个结点上记录该线段中所有元素的最小值。

下面是代码:这里o是当前结点的编号,L和R是当前结点的左右端点,比如,当o=3时,L=5,R=8。查询时全局变量ql和qr分别代表查询区间的左右端点。修改时全局变量p和v分别代表修改点位置和修改后的数值。

int ql,qr;
int query (int o, int L, 3int R){
	int M = L+(R-L)/2,ans = INF;
	if(ql<=L && R <= qr) return minv[o];
	if(ql <= M) ans = min(ans, query(o*2, L, M));
	if(M < qr) ans = min(ans, query(o*2+1, M+1, R));
	return ans;
}

int p,v //修改A[p] = v;
void update (int o, int L, int R){
	int M = L + (R-L)/2;
	if(L==R) minv[o] = v;
	else[  // L < R
		if(p <= M) update(o*2, L, M); else update(o*2+1,M+1,R);
		//计算本结点的minv值
		minv[o] = min(minv[o*2],minv[o*2+1]);
	}
}

最后叙述一下建树的过程。一种方法是每读入一个元素x后执行修改操作a[i]=x,则时间复杂度为O(n*logn)。其实只需要事先设置好每个叶结点的值,自底向上递推即可,每个结点仅计算一次,因此时间复杂度为O(n)。


例题:动态最大连续和

给出一个长度为n的整数序列D,我们的任务是对m个询问做出回答。对于询问(a,b),需要找到两个下标x和y,使得a<=x<=y<=b,并且D[x]+D[x+1]+...+D[y]尽量大。如果存在多组满足条件的x和y,x应该尽量小。如果还有多解,y应该尽量小。

分析:本题看上去很像RMQ问题,但稍微琢磨一下就会发现本题和RMQ的重要区别:整个区间的解不能简单地通过各个子区间的解合并得到。

构造一个线段树,其中每个结点维护三个值:最大连续和max_sub,最大前缀和max_prefix,与最大后缀和max_suffix。具体的说max_sub(a,b)满足a<=x<=y<=b,且D[x]+D[x+1]+...+D[y]最大的二元组。max_prefix(a,b),满足a<=x<=b且D[a]+D[a+1]+D[a+2]+...+D[x]最大的整数。max_suffix(a,b)是满足a<=x<=b且D[x]+D[x+1]+...+D[b]最大的整数x。

比如,n=64,询问为(20,50),则线段[20,50]在线段树的根结点处被分成了两条线段[20,32]和[33,50]。则max_sub(20,50)的起点和终点有3种情况。

1,起点和终点都在[20,32]中,则max_sub(20,50)=max_sub(20,32)

2,起点和终点都在[33,50]中,则max_sub(20,50)=max_sub(33,50)

3,起点在[20,32]中,终点在[33,50]中。则max_sub(20,50)=(max_suffix(20,32),max_prefix(33,50))。

类似的,max_prefix和max_suffix也可以这样递推。建树的复杂度为O(n),单组查询的复杂度为O(logn)。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值