poj2823 -- Sliding Window

16 篇文章 0 订阅
6 篇文章 0 订阅
题目大意:有一个长度为n(n≤10^6)的数列,依次查询区间[1,k],[2,k+1],[3,k+2]……[n-k+1,n]的最值。
显然,每次直接求k个数的最值, 时间复杂度达到O(n*k), 会TLE。
所以,我们要换位思考。
其实我们可以很容易地发现:第一次查询时,建立一棵范围为[1,k]的胜者树即可。
而第二次我们要做的就是:把第1个元素从这棵 树中删去,把第k+1个元素插入这棵树中。
这一过程可以简化为:把第1个元素所在的结点的值更换为第k+1个元素的值。之后,重复这个操作即可。
至此,问题迎刃而解。
注意一个细节:本题要查询的最值包括最大值和最小值。需要建立两棵树吗?不用。只需让每个结点储存两个最值即可。
代码:
# include <cstdio>

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

const int SIZE = 1000000+5 ;

struct Tnode {
	int _min , _max ;
} tree[SIZE<<1];

int n , k , a[SIZE] , ans[2][SIZE] ;
//ans[0][i]表示区间[i,i+k]的最小值 //ans[1][i]表示区间[i,i+k]的最大值 
void build_tree ( int , int , int ) ; //建树
void _insert ( int , int , int , int , int ) ; //修改+维护

int main () {
	scanf ( "%d%d" , &n , &k ) ;
	for ( int i=1 ; i!=n+1 ; ++i )
		scanf ( "%d" , &a[i] ) ;
	build_tree ( 1 , k , 1 ) ; 
	ans[0][0] = tree[1]._min ;
	ans[1][0] = tree[1]._max ;
	for ( int i=1 ; i!=n-k+1 ; ++i ) {
		//第i次要把第i个元素所在的结点的值替换为第i+k个元素
		//第1个元素在1号结点,第2个元素在2号结点……第k个元素在k号结点
		//第(k+1)个元素在1号结点,第(k+2)个元素在2号结点……
		//第i个元素在((i-1)%k)+1号结点。  
		_insert ( 1 , k , ((i-1)%k)+1 , a[i+k] , 1 ) ;
		ans[0][i] = tree[1]._min ;
		ans[1][i] = tree[1]._max ;
	}
	
	for ( int i=0 ; i!=2 ; ++i ) {
		for ( int j=0 ; j!=n-k+1 ; ++j )
			printf ( "%d " , ans[i][j] ) ;
		printf ( "\n" ) ;
	}
	return 0 ;
}

void build_tree ( int _left , int _right , int _node ) {
	if ( _left == _right )
		tree[_node]._min = tree[_node]._max = a[_left] ;
	else {
		int _middle = (_left+_right) >> 1 ;
		int l_child = _node<<1 ; //左子树根结点编号
		int r_child = l_child+1 ; //右子树根结点编号
		
		build_tree ( _left , _middle , l_child ) ;
		build_tree ( _middle+1 , _right , r_child ) ;
		
		tree[_node]._min = min ( tree[l_child]._min , tree[r_child]._min ) ;
		tree[_node]._max = max ( tree[l_child]._max , tree[r_child]._max ) ;
	}
	return ;
}

void _insert ( int _left , int _right , int _k , int new_value , int _node ) {
	if ( _left == _right ) //找到要被修改的结点 
		tree[_node]._min = tree[_node]._max = new_value ;
	else {
		int _middle = (_left+_right) >> 1 ;
		int l_child = _node<<1 ;
		int r_child = l_child+1 ;
		
		if ( _k <= _middle ) //判断要修改的是在左子树还是右子树 
			_insert ( _left , _middle , _k , new_value , l_child ) ;
		else
			_insert ( _middle+1 , _right , _k , new_value , r_child ) ;
		
		//维护 
		tree[_node]._min = min ( tree[l_child]._min , tree[r_child]._min ) ;
		tree[_node]._max = max ( tree[l_child]._max , tree[r_child]._max ) ;
	}
	return ;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值