线段树1

Posted chen-1

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线段树1相关的知识,希望对你有一定的参考价值。

先讲解一下线段树

线段树用于区间操作的优化,来看一道这样的题:

https://www.luogu.org/problem/P2068

在一个序列中,支持以下操作:修改序列中一个数,查询其中y一段区间的和

元素数<=100000,操作数<=10000.

由于数据较大,n*m显然过不了,这时,就要用到线段树了:

技术图片

这就是线段树示意图

每个节点上都有一个区间,这个节点的点权就是这个区间所有数的和

但不用担心初始化的时间,因为每个节点的点权等于它的左右儿子点权之和

一个节点的左右儿子的点权分别对应l~(l+r)/2的和与(l+r)/2+1~r的和

到叶节点时这个点权就是对应这个数。

所以build就可以这样写

void build(int l,int r,int root)

	if(l==r) 
	
		num[root]=a[l];
		return;
	
	int mid=(l+r)/2;
	build(l,mid,root*2);
	build(mid+1,r,root*2+1);
	num[root]=min(num[root*2],num[root*2+1]);

比如,我要查询的是3~7这个区间,那它的和就是

技术图片

 

这三个节点的点权之和

现在我们看一下查询的时间复杂度,很明显大约是O(logn)

现在,来看一下查询的代码

int search(int p,int q,int l,int r,int root)

	if(p==l && r==q)
	
		return num[root];
	
	int mid=(l+r)/2;
	if(p>=mid+1) return search(p,q,mid+1,r,root*2+1);
	else if(q<=mid) return search(p,q,l,mid,root*2);
	else return search(p,mid,l,mid,root*2)+search(mid+1,q,mid+1,r,root*2+1);

代码解释

如果要查询的区间被该节点的左子树或右子树完全包含,就只用查找一个子树就行了

否则要查询的区间一定是跨越了两个子树,这样就要查询两次

如果要查询的区间与该节点对应的区间刚好重合,这样就直接return这个值就好了

 

最后是修改,例如我要修改6这个节点,那要修改的就是

技术图片

这五个节点,时间复杂度也是O(logn)(因为每层一个)

简单看一下代码

void change(int p,int val,int l,int r,int root)

	if(l==r)
	
		num[root]+=val;
		return;
	
	int mid=(l+r)/2;
	if(p>=mid+1) change(p,val,mid+1,r,root*2+1);
	else change(p,val,l,mid,root*2);
	num[root]=num[root*2]+num[root*2+1];

代码解释

如果要查询的节点再该节点的左子树,就查找一个左子树就行了

否则就查找一个右子树就好了

如果要查询的节点与该节点刚好重合,这样就直接修改这个值就好了

 

最后是这道题的完整代码

#include<bits/stdc++.h>
using namespace std;
int num[100000*4];
void build(int l,int r,int root)

	if(l==r)
	
		num[root]=0;
		return;
	
	int mid=(l+r)/2;
	build(l,mid,root*2);
	build(mid+1,r,root*2+1);
	num[root]=num[root*2]+num[root*2+1];

void change(int p,int val,int l,int r,int root)

	if(l==r)
	
		num[root]+=val;
		return;
	
	int mid=(l+r)/2;
	if(p>=mid+1) change(p,val,mid+1,r,root*2+1);
	else change(p,val,l,mid,root*2);
	num[root]=num[root*2]+num[root*2+1];

int search(int p,int q,int l,int r,int root)

	//printf("%d %d %d %d %d\\n",p,q,l,r,root);
	if(p==l && r==q)
	
		return num[root];
	
	int mid=(l+r)/2;
	if(p>=mid+1) return search(p,q,mid+1,r,root*2+1);
	else if(q<=mid) return search(p,q,l,mid,root*2);
	else return search(p,mid,l,mid,root*2)+search(mid+1,q,mid+1,r,root*2+1);

int main()

	int n,w;
	scanf("%d%d",&n,&w);
	for(int i=1;i<=w;i++)
	
		char t;
		int x,y;
		cin>>t>>x>>y;
		if(t==‘x‘)
		
			change(x,y,1,n,1);
		
		else if(t==‘y‘)
		
			printf("%d\\n",search(x,y,1,n,1));
		
	
	return 0;

最后再给大家推荐几道线段树的养生的模板

https://www.luogu.org/problem/U85066

https://www.luogu.org/problem/P1637

 

以上是关于线段树1的主要内容,如果未能解决你的问题,请参考以下文章

线段树

比较简单的线段树入门

zkw线段树

权值线段树&&线段树合并

线段树详解

线段树P3372模板-线段树