poj3264 -- Balanced Lineup

16 篇文章 0 订阅
6 篇文章 0 订阅
题目大意:给定n(1 ≤ N ≤ 50,000)个数,每次求指定区间内的最大值减去最小值的差。
分析:这道题目实际上就是一道简单RMQ,可以用st算法解决,也可以用胜者树。
st算法代码如下:
# include <cstdio>

# define max(a,b) a>b?a:b
# define min(a,b) a<b?a:b

const int SIZE = 50000+5 ;

int N , Q , log2[SIZE] , f[2][30][SIZE];
//f[0][i][j]储存以j为起始点的2^i个数的最大值 //f[1][i][j]储存以j为起始点的2^i个数的最小值

void init () ; //初始化,建表
void work () ; //处理查询 
int _query ( int , int ) ; //查询指定区间[a,b]的结果 

int main () {
	init () ;
	work () ;
	return 0 ;
}

void init () {
	scanf ( "%d%d" , &N , &Q ) ;
	for ( int i=1 ; i!=N+1 ; ++i ) {
		scanf ( "%d" , &f[0][0][i] ) ;
		f[1][0][i] = f[0][0][i] ;
		//2^0=1,因此连续2^0个数的最值就是自己本身 
	}
	
	for ( int i=2 ; i!=N+1 ; ++i ) //建立以2为底的对数表 
		log2[i] = log2[i>>1] + 1;
	
	//建表
	for ( int i=1 ; i!=log2[N]+1 ; ++i ) //i表示当前是连续2^i个数的最值
		for( int j=1 ; j<=N-(1<<i)+1 ; ++j ) { //j表示当前起始点
			f[0][i][j] = max ( f[0][i-1][j] , f[0][i-1][j+(1<<(i-1))] ) ;
			f[1][i][j] = min ( f[1][i-1][j] , f[1][i-1][j+(1<<(i-1))] ) ;
		}
	return ;
}

int _query ( int _left , int _right ) {
	int k = log2[_right-_left+1];
	int _max = max(f[0][k][_left],f[0][k][_right-(1<<k)+1]) ;
	int _min = min(f[1][k][_left],f[1][k][_right-(1<<k)+1]) ;
	return _max - _min ;
}

void work () {
	for ( int i=0; i!=Q ; ++i ) {
		int _l , _r ;
		scanf ( "%d%d" , &_l , &_r ) ;
		printf ( "%d\n" , _query ( _l , _r ) ) ;
	}
	return ;
}
当然除了用st算法,也可以用胜者树实现此题,具体如下:
当我们查询区间[a,b]内的最值并定义m=(1+n)/2时,显然有三种情况:
  1. 区间[a,b]完全在[1,m]内。
  2. 区间[a,b]完全在[m+1,n]内。
  3. 区间[a,b]被分为[a,m]和[m+1,b]两个区间。
前两种情况,我们都可以直接通过建树得知最值;但第三种情况则需要稍稍处理一下:取两个区间内的最大值为[a,b]的最大值,取最小值亦同。
代码如下:
# include <cstdio>

#define min(x,y) x<y?x:y
#define max(x,y) x>y?x:y

const int SIZE = 500005 ;

struct Tnode {
	int min_value , max_value ;
} tree[SIZE<<1];

int N , Q , h[SIZE] ;
int A[200005] , B[200005] ;

void init();
void buildtree ( int , int , int ) ;
Tnode query ( int , int , int , int , int ) ;

int main () {
	init();
	buildtree ( 1 , N , 1 ) ;
	for ( int i=0 ; i!=Q ; ++i ) {
		Tnode tmp=query(1,N,A[i],B[i],1);
		printf ( "%d\n" , tmp.max_value-tmp.min_value ) ;
	}
	return 0 ;
}

void init () {
	scanf ( "%d%d" , &N , &Q ) ;
	for ( int i=1 ; i!=N+1 ; ++i )
		scanf ( "%d" , &h[i] ) ;
	for ( int i=0 ; i!=Q ; ++i )
		scanf ( "%d%d" , &A[i] , &B[i] ) ;
	return ;
}

void buildtree ( int left , int right , int node ) {
	if ( left == right ) {
		tree[node].min_value = tree[node].max_value = h[left] ;
	} else {
		int middle = ( left + right ) >> 1 ;
		buildtree ( left , middle , node<<1 ) ;
		buildtree ( middle+1 , right , (node<<1)+1 ) ;
		tree[node].min_value = min ( tree[node<<1].min_value , tree[(node<<1)+1].min_value ) ;
		tree[node].max_value = max ( tree[node<<1].max_value , tree[(node<<1)+1].max_value ) ;
	}
	return ;
}

Tnode query ( int left , int right , int low , int high , int node ) {
//查询区间[low,high]的最值 //保证left<=low且right>=high,即[low,high]一定在[left,right]内 
	if ( left==low&&right==high ) return tree[node]; //找到目标 
	int middle = ( left + right ) >> 1 ;
	if ( high <= middle ) return query ( left , middle , low , high , node<<1 ) ; //情况1 
	else if ( low > middle ) return query ( middle+1 , right , low , high , (node<<1)+1 ) ;//情况2 
	Tnode res,le,ri;
	le=query(left , middle , low , middle , node<<1); //求左区间 
	ri=query(middle+1 , right , middle+1 , high , (node<<1)+1); //求右区间 
	res.min_value=min(le.min_value,ri.min_value);
	res.max_value=max(le.max_value,ri.max_value);
	return res;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值