线段树合集

定义:

线段树是建立在线段的基础上,每个结点都代表了一条线段[a , b]。长度为1的线段称为元线段。非元线段都有两个子结点,左结点代表的线段为[a , (a + b ) / 2],右结点代表的线段为[( (a + b ) / 2)+1 , b]。

基本操作:

建树,插入,删除,查找

线段树的特征:

1、线段树的深度不超过logL(L是最长区间的长度)。

2、线段树把区间上的任意一条线段都分成不超过2logL条线段。
这些结论为线段树能在O(logL)的时间内完成一条线段的插入、删除、查找等工作,提供了理论依据


优化:

区间的端点不是整数,或者区间太大导致建树内存开销过大MLE ,那么就需要进行“离散化”后再建树。

例题:

1,POJ 3264 Balanced Lineup

给定Q(1 ≤ Q≤ 200,000)个数A1,A2… AQ,,多次求任一区间Ai–Aj中最大数和最小数的差。

#include<iostream>
using namespace std;
#define MY_MIN 1 << 31 - 1
#define MY_MAX 1- 1 << 31

struct CNode
{
	int L,R;
	int nMin,nMax;
	CNode * pLeft,* pRight;
};
int nMin,nMax;
CNode Tree[1000001];
int nCount = 0;

void BuildTree(CNode * pRoot,int L,int R)
{
	pRoot->L = L;pRoot->R = R;
	pRoot->nMin = MY_MIN;pRoot->nMax = MY_MAX;
	
	if(L!=R)
	{
		nCount ++;
		pRoot->pLeft =Tree + nCount;
		nCount ++;
		pRoot->pRight =Tree + nCount;

		BuildTree(pRoot->pLeft,L,(L+R) / 2);
		BuildTree(pRoot->pRight,(L+R) / 2 + 1,R);
	}
}
void Insert(CNode * pRoot,int i,int v)
{
	if(pRoot->L == i && pRoot->R == i)
	{
		pRoot->nMin = pRoot->nMax = v;
		return;
	}
	pRoot->nMin =_cpp_min(pRoot->nMin,v);
	pRoot->nMax =_cpp_max(pRoot->nMax,v);
	
	if(i <= (pRoot->L + pRoot->R) / 2)
		Insert(pRoot->pLeft,i,v);
	else
		Insert(pRoot->pRight,i,v);
}
void Query(CNode * pRoot,int s,int e)
{
	if( pRoot->nMin >= nMin && pRoot->nMax <= nMax )
		return;
	if( s == pRoot->L && e == pRoot->R) {
		nMin = _cpp_min(pRoot->nMin,nMin);
		nMax = _cpp_max(pRoot->nMax,nMax);
		return ;
	}
	if( e <=  (pRoot->L + pRoot->R) / 2 )
		Query(pRoot->pLeft, s,e);
	else if( s >= (pRoot->L + pRoot->R) / 2 + 1)
		Query(pRoot->pRight, s,e);
	else {
		Query(pRoot->pLeft, s,(pRoot->L + pRoot->R) / 2);
		Query(pRoot->pRight, (pRoot->L + pRoot->R) / 2 + 1 ,e);
	}
}
int main()
{
	int n,q,v;
	int i;
	cin>>n>>q;
	nCount = 0;
	BuildTree(Tree,1,n);
	for(i = 1;i <= n;i++)
	{
		scanf("%d",&v);
		Insert(Tree,i,v);
	}

	for(i = 0;i < q;i++)
	{
		int s,e;
		scanf("%d %d",&s,&e);
		nMin = MY_MIN;
		nMax = MY_MAX;
		Query(Tree,s,e);
		printf("%d\n",nMax - nMin);
	}
	return 0;
}

2.POJ 3468 A Simple Problem with Integers

给定Q(1 ≤ Q≤ 100,000)个数A1,A2… AQ,,以及可能多次进行的两个操作:1)对某个区间Ai … Aj的个数都加n(n可变)2) 求某个区间Ai … Aj的数的和


只存和,会导致每次加数的时候都要更新到叶子节点,速度太慢,这是必须要避免的。

#include <iostream>
using namespace std;

struct CNode 
{
	int L ,R;
	CNode * pLeft, * pRight;
	__int64 nSum;		//原来的和
	__int64 Inc;		//增量c的累加
};

CNode Tree[10000000];

int nCount = 0;
int Mid( CNode * pRoot)
{
	return (pRoot->L + pRoot->R)/2;
}
void BuildTree(CNode * pRoot,int L, int R)
{
	pRoot->L = L;
	pRoot->R = R;
	pRoot->nSum = 0;
	pRoot->Inc = 0;
	if( L == R)
		return;
	nCount ++;
	pRoot->pLeft = Tree + nCount;
	nCount ++;
	pRoot->pRight = Tree + nCount;
	BuildTree(pRoot->pLeft,L,(L+R)/2);
	BuildTree(pRoot->pRight,(L+R)/2+1,R);
}
void Insert( CNode * pRoot,int i, int v)
{
	if( pRoot->L == i && pRoot->R == i) {
		pRoot->nSum = v;
		return ;
	}
	pRoot->nSum += v;
	if( i <= Mid(pRoot))
		Insert(pRoot->pLeft,i,v);
	else
		Insert(pRoot->pRight,i,v);
}
void Add( CNode * pRoot, int a, int b, __int64 c)
{
	if( pRoot->L == a && pRoot->R == b) {
		pRoot->Inc += c;
		return ;
	}
	pRoot->nSum += c * ( b - a + 1) ;
	if( b <= (pRoot->L + pRoot->R)/2)
		Add(pRoot->pLeft,a,b,c);
	else if( a >= (pRoot->L + pRoot->R)/2 +1)
		Add(pRoot->pRight,a,b,c);
	else {
		
		Add(pRoot->pLeft,a,(pRoot->L + pRoot->R)/2 ,c); 
		Add(pRoot->pRight,(pRoot->L + pRoot->R)/2 + 1,b,c);
	}
}

__int64 QuerynSum( CNode * pRoot, int a, int b)
{
	if( pRoot->L == a && pRoot->R == b)
		return pRoot->nSum + (pRoot->R - pRoot->L + 1) * pRoot->Inc ;

	pRoot->nSum += (pRoot->R - pRoot->L + 1) * pRoot->Inc ;
	Add( pRoot->pLeft,pRoot->L,Mid(pRoot),pRoot->Inc);
	Add( pRoot->pRight,Mid(pRoot) + 1,pRoot->R,pRoot->Inc);
	pRoot->Inc = 0;

	if( b <= Mid(pRoot)) 
		return QuerynSum(pRoot->pLeft,a,b);
	else if( a >= Mid(pRoot) + 1) 
		return QuerynSum(pRoot->pRight,a,b);
	else {
		return QuerynSum(pRoot->pLeft,a,Mid(pRoot)) + 
			   QuerynSum(pRoot->pRight,Mid(pRoot) + 1,b);
	}
}
int main()
{
	int n,q,a,b,c;
	char cmd[10];
	
	scanf("%d%d",&n,&q);
	int i,j,k;
	nCount = 0;
	BuildTree(Tree,1,n);
	for( i = 1;i <= n;i ++ ) {
		scanf("%d",&a);
		Insert(Tree,i,a);
	}
	for( i = 0;i < q;i ++ ) {
		
		scanf("%s",cmd);
		if ( cmd[0] == 'C' ) {
			scanf("%d%d%d",&a,&b,&c);
			Add( Tree,a,b,c);
		}
		else {
			scanf("%d%d",&a,&b);
			printf("%I64d\n",QuerynSum(Tree,a,b));
		}
	}
	return 0;
}

3. POJ 2528 Mayor's posters

#include <iostream>
#include <algorithm>
using namespace std;
int n;
struct CPost
{
	int L,R;
};
CPost posters[10100];
int x[20200];
int hash[10000010];
struct CNode 
{
	int L,R;
	bool bCovered;		//本区间是否已经被完全覆盖 
	CNode * pLeft, * pRight;
};
CNode Tree[100000];
int nNodeCount = 0;
int Mid( CNode * pRoot)
{
	return (pRoot->L + pRoot->R)/2;
}
void BuildTree( CNode * pRoot, int L, int R)
{
	pRoot->L = L;
	pRoot->R = R;
	pRoot->bCovered = false;
	if( L == R )
		return;
	nNodeCount ++;
	pRoot->pLeft = Tree + nNodeCount;
	nNodeCount ++;
	pRoot->pRight = Tree + nNodeCount;
	BuildTree( pRoot->pLeft,L,(L+R)/2);
	BuildTree( pRoot->pRight,(L+R)/2 + 1,R);
}
bool Post( CNode  *pRoot, int L, int R)
{
	if( pRoot->bCovered )
		return false;
	if( pRoot->L == L && pRoot->R == R) {
		pRoot->bCovered = true;
		return true;
	}
	bool bResult ;
	if( R <= Mid(pRoot) ) 
		bResult = Post( pRoot->pLeft,L,R);
	else if( L >= Mid(pRoot) + 1)
		bResult = Post( pRoot->pRight,L,R);
	else {
		bool b1 = Post(pRoot->pLeft ,L,Mid(pRoot));
		bool b2 = Post( pRoot->pRight,Mid(pRoot) + 1,R);
		bResult = b1 || b2;
	}
	//要更新根节点的覆盖情况
	if( pRoot->pLeft->bCovered && pRoot->pRight->bCovered )
		pRoot->bCovered = true;
	return bResult;
}
int main()
{
	int t;
	int i,j,k;
	scanf("%d",&t);
	int nCaseNo = 0;
	while(t--) {
		nCaseNo ++;
		scanf("%d",&n);
		int nCount = 0;
		for( i = 0;i < n;i ++ )  {
			scanf("%d%d", & posters[i].L,& posters[i].R );

			x[nCount++] = posters[i].L;
			x[nCount++] = posters[i].R;
		}
		sort(x,x+nCount);
		nCount = unique(x,x+nCount) - x;	//去掉重复元素
		for( i = 0;i < nCount;i ++ )
			hash[x[i]] = i;
		nNodeCount = 0;
		BuildTree( Tree,0,nCount - 1);
		int nSum = 0;
		for( i = n-1;i >= 0;i -- ) {		// 从后往前看板是否看得见
			if( Post(Tree,hash[posters[i].L],hash[posters[i].R]))
				nSum ++;
		}
		printf("%d\n",nSum);
	}
	return 0;
}



 
 
 
 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值