HDOJ 2795 中级线段树 线段树性质的灵活运用

题目大意:给定一面墙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;
}


你可能感兴趣的:(数据结构,c,Build,n2)