HDU 1754 线段树 及其 入门知识

刚开始以为看到这道题是提交,ac的人很多,以为是水题,用最简单的方法操作,超时!

线段树!!


线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。

对于线段树中的每一个非叶子节点[a,b],它的左儿子表示的区间为[a,(a+b)/2],右儿子表示的区间为[(a+b)/2+1,b]。因此线段树是平衡二叉树,最后的子节点数目为N,即整个线段区间的长度。

使用线段树可以快速的查找某一个节点在若干条线段中出现的次数,时间复杂度为O(logN)。而未优化的空间复杂度为2N,因此有时需要离散化让空间压缩。

实际应用:(源自百度百科)

最简单的应用就是记录线段有否被覆盖,并随时查询当前被覆盖线段的总长度。那么此时可以在结点结构中加入一个变量int count;代表当前结点代表的子树中被覆盖的线段长度和。这样就要在插入(删除)当中维护这个count值,于是当前的覆盖总值就是根节点的count值了。

  另外也可以将count换成bool cover;支持查找一个结点或线段是否被覆盖。 [1]
  实际上,通过在结点上记录不同的数据,线段树还可以完成很多不同的任务。例如,如果每次插入操作是在一条线段上每个位置均加k,而查询操作是计算一条线段上的总和,那么在结点上需要记录的值为sum。
  这里会遇到一个问题:为了使所有sum值都保持正确,每一次插入操作可能要更新O(N)个sum值,从而使时间复杂度退化为O(N)。
  解决方案是 Lazy思想:对整个点进行的操作,先在结点上做标记,而并非真正执行,直到根据查询操作的需要分成两部分。
  根据Lazy思想,我们可以在不代表原线段的结点上增加一个值toadd,即为对这个结点,留待以后执行的插入操作k值的总和。对整个结点插入时,只更新sum和toadd值而不向下进行,这样时间复杂度可证明为O(logN)。
  对一个toadd值为0的结点整个进行查询时,直接返回存储在其中的sum值;而若对toadd不为0的一部分进行查询,则要更新其左右子结点的sum值,然后把toadd值传递下去,再对这个查询本身,左右子结点分别 递归 下去。时间复杂度也是O(logN)。


#include <iostream>
using namespace std;
struct Node
{
	int l,r,mid,max;
}node[600000];

void init(int a,int b,int n)
{
	node[n].l=a;
	node[n].max=-1;
	node[n].r=b;
	node[n].mid=(a+b)/2;

	if(a+1==b)
		return;
	init(a,(a+b)/2,n*2);
	init((a+b)/2,b,2*n+1);
}

void update(int pos,int value,int n)
{
	if(value>node[n].max)
		node[n].max=value;
	if((node[n].l+1)==node[n].r)
		return;
	if(pos<node[n].mid)
	{
		update(pos,value,2*n);
	}
	if(pos>=node[n].mid)
	{
		update(pos,value,2*n+1);
	}
}

int query(int a,int b,int n)
{
	if(node[n].l==a && node[n].r==b)
		return node[n].max;
	if(a<node[n].mid)
	{
		if(b<=node[n].mid)
		{
			return query(a,b,2*n);
		}
		else
		{
			return max(query(a,node[n].mid,2*n),query(node[n].mid,b,2*n+1));
		}
	}
	else
	{
		return query(a,b,2*n+1);
	}
}

int main()
{
	int n,m,t,a,b,i;
	char ch;
	while(cin >>n>>m)
	{
		init(1,n+1,1);
		for(i=1;i<=n;i++)
		{
			scanf("%d",&t);
			update(i,t,1);
		}            
		for(i=1;i<=m;i++)
		{
			getchar();
			cin >>ch;
			cin >>a>>b;
			if(ch=='Q')
				cout <<query(a,b+1,1)<<endl;//注意是b+1而不是b。因为线段树是一个左开右闭的的区间。
			else
				update(a,b,1);
		}
	}
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值