题目大意:给定一面墙h*w的尺寸,现在向墙壁粘贴1*w'的纸条,求字典序最小的而且满足条件的粘贴位置。
这题h,w的范围为10^9而普通的线段树是要此尺寸*4的,刚开始写的时候内存开大了,但是题目的另外一个条件n<200000,询问n次,所以线段树不需要开到10^9,即便是每次询问都重新开一行,最多才200000行,而不会达到恐怖的10^9,故对h进行约束就可以了。
各种线段树的关键在于分析题目要的是什么,在区间合并线段树中,采用的是左右中三个标记数组,类型判重线段树中则是用一个函数进行或操作,达到合并类型的效果,而这题每次的查找一定都是叶子节点,当前节点记录的是下层节点能够使用空间的最大值,如果当前节点能够提供所需空间,则判断是左子树还是右子树满足,左子树满足直接递归左子树,否则右子树,保证字典序。当前节点不满足的话,输出-1就够了。这个线段树没有PushUp操作,因为在update过程中可以实现出栈更新,线段树只是一种数据结构,具体怎么用添加什么函数,什么标记都凭借个人的理解。
初步感受二叉树的美...
#include<iostream>
#define MAXN 200001
using namespace std;
struct node
{
int left,right,val;
}sum[MAXN<<2];
int h,w,n;
int max( int a,int b ){ return a>b?a:b; }
void build( int l,int r,int rt )
{
sum[rt].left=l;
sum[rt].right=r;
sum[rt].val=w;
if( l==r )
return ;
int m=(l+r)>>1;
build( l,m,rt<<1 );
build( m+1,r,rt<<1|1 );
}
void update( int c,int rt )
{
if( c<=sum[rt].val )
{
if( sum[rt].left==sum[rt].right )
{
sum[rt].val-=c;
printf( "%d\n",sum[rt].left );
return ;
}
else if( sum[rt<<1].val>=c )
{
update( c,rt<<1 );
sum[rt].val=max( sum[rt<<1].val,sum[rt<<1|1].val );
}
else
{
update( c,rt<<1|1 );
sum[rt].val=max( sum[rt<<1].val,sum[rt<<1|1].val );
}
}
else
{
printf( "-1\n" );
}
}
int main()
{
int d;
while( scanf( "%d %d %d",&h,&w,&n)!=EOF )
{
// printf( "!" );
if( h>MAXN )
h=MAXN;
build( 1,h,1 );
while( n-- )
{
scanf( "%d",&d );
update( d,1 );
}
}
return 0;
}