poj3264 poj3468 poj2528【线段树】

线段树

    操作:

    区间分解(LogN):

        如果有某个节点代表的区间完全属于待分解区间,则该节点为“终止节点” 不再继续向下分解。

        所有终止节点不重叠,加在一起恰等于整个待分解区间。

 

    伪代码:

   

function (根节点pRoot,查询区间[l,r]){
   if(查询区间恰等于节点表示区间)
        return;
    if(查询区间都在节点中点左侧)
	function(pRoot左儿子,查询区间[l,r]);
   else if(查询区间都在节点中点左侧)
	function(pRoot右儿子,查询区间[l,r]);
   else {
	function(pRoot左儿子,查询区间[l,mid]);
	function(pRoot右儿子,查询区间[mid+1,r]);
   }
}

 

poj3264

本题中线段树的每个节点要记录该区间内的最大值和最小值。

在查询时,采用区间分解的操作,取得整个查询区间的最值。

#include<iostream>
#include<algorithm>
using namespace std;

#define MY_MIN 99999999
#define MY_MAX -99999999

struct CNode{
	int L,R;
	int nMin,nMax;
	CNode *pLeft ,* pRight;
};

int nMax,nMin;

CNode Tree[1000000];
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) 
//将第i个数,其值为v,插入线段树 
{ 
           if( pRoot->L == i &&  pRoot->R == i ) { 
                       pRoot->nMin = pRoot->nMax = v; 
                       return; 
           } 
           pRoot->nMin = min(pRoot->nMin,v); 
           pRoot->nMax =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) 
//查询区间[s,e]中的最小值和最大值,如果更优就记在全局变量里 
{ 
         if( pRoot->nMin >= nMin && pRoot->nMax <= nMax ) 
                  return; 
         if( s == pRoot->L && e == pRoot->R) { 
                  nMin = min(pRoot->nMin,nMin); 
                  nMax =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,h; 
               int i,j,k; 
               scanf("%d%d",&n,&q); 
               nCount = 0; 
               BuildTree(Tree,1,n); 
               for( i = 1;i <= n;i ++ ) { 
                             scanf("%d",&h); 
                              Insert( Tree,i,h); 
               } 
               for( i = 0;i < q;i ++ ) { 
                              int s,e; 
                             scanf("%d%d", &s,&e); 
                              nMax = MY_MAX; 
                              nMin = MY_MIN; 
                             Query(Tree,s,e); 
                              printf("%d\n",nMax - nMin); 
               } 
               return 0; 
} 


poj3468

本题有两个操作。

1.对区间内所有数加n

2.对区间求和

 

本题中每个节点分别维护Sum和Inc

Inc作为增量的表示,即真正的和等于Sum+len*Inc    (len为节点所表示区间长度)

#include <iostream>
using namespace std;


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

CNode Tree[1000000];

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, long long 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);
	}
}

long long 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;
}


poj2528

本题若以瓷砖端点为区间建立线段树,肯定会MLE

所以要用到离散化的方法。

以海报端点建立区间。

Insert的过程就是区间分解的过程。

 

#include<iostream>
#include<algorithm>
#include<cmath>
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[1000000];
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;
	scanf("%d",&t);
	int nCaseNo = 0;
	while(t--){
		nCaseNo ++;
		cin>>n;
		int nCount=0;
		for(int i=0;i<n;i++){
			cin>>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;
		
		int nIntervalNo=0;
		for(int i=0;i<nCount;i++){
			hash[x[i]]=nIntervalNo;
			if(i<nCount-1){
				if(x[i+1]-x[i]==1)
					nIntervalNo++;
				else nIntervalNo+=2;
			}
		}
		BuildTree(Tree,0,nIntervalNo);
		int nSum=0;
		for(int i=n-1;i>=0;i--){
			if(Post(Tree,hash[posters[i].L],hash[posters[i].R]))
				nSum++;
		}
		cout<<nSum<<endl;
	}
	return 0;
}


 


 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值