HDOJ1199 Color the Ball 线段树+离散化

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1199

题目大意:有编号1,2,3,4。。。。。无限多的球,输入N个命令,每个命令格式为num1 num2 w/b。意思是把num1~num2的所有球涂成白色或者黑色。最后输出最长的白色序列。

显而易见这个一个区间查询问题,所以自然而然想到利用线段树来解题。我们首先解决的是线段树区间合并的问题。

线段树区间合并问题:例如,所有的命令都操作范围在3~10的球,那么最长白色序列就是求3~10最长白色序列。设mid=(3+10)/2=6。那么:

最长白色序列=max{ 3~6最长白色序列  , 7~10最长白色序列  ,  横跨两个区间的最长白色序列}

所以线段树的节点维护以下信息:l,r:线段树表示的区间。wl,wr:wl~wr为区间l~r上最长白色区间。 left_end:若l为白色,left_end为以l开头白色序列结尾的数字。right_start:若r为白色,right_start为以r结尾白色序列的第一个数字。

所以设当前线段树节点为node[index],则:

node[index]维护的区间上的最长白色序列=max{ node[index<<1].wr-node[index<<1].wl+1 , node[index<<1|1].wr-node[index<<1|1].wl+1 ,,node[index<<1|1].left_end-node[index<<1].right_start+1(如果存在横跨两个子区间的白色序列的话)}

接着,我们需要将输入的点做离散化处理。本题的输入都在int类型的范围内,如果线段树直接维护所有输入的数字的话,范围非常大,例如我输入1 999999 w,则线段树的根节点维护的是1 999999,浪费了大量的内存空间。如果我们对输入进行离散化的话,则可以节省内存空间,当我们输入:

3
1 19 w
4 19 b
12 30 w

首先对所有点进行排序去重,得到1,4,12,19,30,把这5个数据保存到point数组中,则数据为point[1]~point[5],线段树的根节点维护1~5,如果不进行离散化,线段树的根节点维护1~30,浪费内存空间。

那么现在是否OK了呢?现在依然存在问题。考虑这组输入:

2
1 4 w
6 7 w

则最长白色序列应该是1 4。如果按照上述算法执行,会有怎样的结果?

首先对点进行排列去重:point[1]~point[4]={1,4,6,7}。建立线段树:

node[1]维护[1,4],node[2]维护[1,2],node[3]维护[3,4]。。。。。。首先point[1]~point[2]涂成白色,更新node[2]的信息:

node[2].wl=1,node[2].wr=2,node[2].left_end=2,node[2].right_start=1。

接着point[3]~point[4]涂成白色,更新node[3]的信息:node[3].wl=3,node[3].wr=4 , node[3].left_end=4 , node[3].right_start=3。

这个时候更新node[1]的信息就会出错,node[1].wl=1, node[1].wr=4所以最长白色区间为point[1]~point[4]。出错的原因就在于5其实是黑色,但是并没有体现出来,所以对所有点进行排序去重以后,需要在4后面加上一个5才行。于是,对point数组的所有点进行排序去重以后,若point[i]和point[i-1]差大于1,则需要在两者中间插入point[i-1]+1,所以上述点排序去重以后得到的point数组为:1,2,4,5,6,7。可以得到正确结果。

但是现在依然没有OK,考虑这样的一组输入:

2
8 29 w
16 22 b

执行完上述两个命令后8~15和23~29是白色,但是如果按照上述算法运行呢?首先,排序去重后得到8,9,16,17,22,23,29(point[1]~point[7])首先将point[1]~point[7]涂成白色,接着将point[3]~point[5]涂成黑色。此时point[1]~point[2]白色,point[6]~point[7]白色,所以最长的白色序列为point[6]~point[7]为23~29。这里出问题的原因在涂黑的过程中,没有体现出15依然是白色,所以出错。那么在point进行排序去重以后,若point[i]-point[i-1]>1,则在point[i]和point[i-1]之间插入两个数字point[i]-1,point[i-1]+1可以解决这个问题。得到point[1]~point[n],线段树根节点维护1~n区间即可。线段树最好使用懒标记,可以有效降低执行时间:

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

const int maxn=4000;

int remove_dup(int *point,int _last);
int biSearch(int a);
//命令,将l~r涂色
struct Command{
  int l,r;
  bool white;
};
Command command[maxn+5];
int point[(maxn+5)*5];//所有的点
int tmp[(maxn+5)*5];
int total;//去重以后总共有total个点
int N;

struct Node{
  int l,r;//区间
  int wl,wr;//l~r区间上最长白色区间
  int left_end;//l~r区间上以l开始最长的白色区间是l~left_end
  int right_start;//l~r区间上以r结尾最长的白色区间right_start-r
  bool lazy;//懒标记,叶子节点懒标记恒为false
  bool lazy_white;//是否为涂白色懒标记
};
struct SegTree{
  Node node[9*maxn];
  void buildTree(int l,int r,int index);
  void paintWhite(int l,int r,int index);//第l小~第r小区间涂白
  void paintBlack(int l,int r,int index);
  void update(int index);//更新wl,wr,left_end,right_start
};
void SegTree::buildTree(int l,int r,int index){
  node[index].l=l;
  node[index].r=r;
  node[index].wl=node[index].wr=node[index].left_end=node[index].right_start=0;//初始全部为0
  node[index].lazy=node[index].lazy_white=false;
  if(l==r) return;
  else{
    int mid=(l+r)/2;
    buildTree(l,mid,index<<1);
    buildTree(mid+1,r,index<<1|1);
  }
}
void SegTree::paintWhite(int l,int r,int index){
  //如果有懒标记,首先把懒标记下推
  //cout<<"paintWhite,node[index].l="<<node[index].l<<"  node[index].r="<<node[index].r<<"  lazy="<<node[index].lazy<<" lazy_white="<<node[index].lazy_white<<endl;

  if(node[index].lazy){
    if(node[index].lazy_white){
      return;
    }else{
      //cout<<"push down black lazy symbol"<<endl;
      node[index<<1].wl=node[index<<1].wr=node[index<<1].left_end=node[index<<1].right_start=0;
      if(node[index<<1].l!=node[index<<1].r){
        node[index<<1].lazy=true;
        node[index<<1].lazy_white=false;
      }
      node[index<<1|1].wl=node[index<<1|1].wr=node[index<<1|1].left_end=node[index<<1|1].right_start=0;
      if(node[index<<1|1].l!=node[index<<1|1].r){
        node[index<<1|1].lazy=true;
        node[index<<1|1].lazy_white=false;
      }
      node[index].lazy=false;
    }
  }

  //cout<<"push down finish"<<endl;

  if(node[index].l==node[index].r){
    node[index].wl=node[index].wr=node[index].left_end=node[index].right_start=node[index].l;
  }else if(node[index].l>=l && node[index].r<=r){

    //cout<<"node[index].l="<<node[index].l<<"  node[index].r="<<node[index].r<<"  ,"<<"all paited white,set lazy=true"<<endl;

    node[index].lazy=node[index].lazy_white=true;
    node[index].wl=node[index].l;
    node[index].wr=node[index].r;
    node[index].left_end=node[index].r;
    node[index].right_start=node[index].l;
  }else {
    int mid=(node[index].l+node[index].r)/2;
    if(l<=mid && r>=node[index].l) paintWhite(l,r,index<<1);
    if(r>=mid+1 && l<=node[index].r) paintWhite(l,r,index<<1|1);
    update(index);
  }
}
void SegTree::paintBlack(int l,int r,int index){
  //cout<<"paintBlack,node[index].l="<<node[index].l<<"  node[index].r="<<node[index].r<<"  lazy="<<node[index].lazy<<" lazy_white="<<node[index].lazy_white<<endl;

  if(node[index].lazy){
    if(!node[index].lazy_white){
      return;
    }else{
      //下推白色懒标记
      node[index<<1].wl=node[index<<1].right_start=node[index<<1].l;
      node[index<<1].wr=node[index<<1].left_end=node[index<<1].r;
      if(node[index<<1].l!=node[index<<1].r){
        node[index<<1].lazy=node[index<<1].lazy_white=true;
      }
      node[index<<1|1].wl=node[index<<1|1].right_start=node[index<<1|1].l;
      node[index<<1|1].wr=node[index<<1|1].left_end=node[index<<1|1].r;
      if(node[index<<1|1].l!=node[index<<1|1].r){
        node[index<<1|1].lazy=true;
        node[index<<1|1].lazy_white=true;
      }
      node[index].lazy=false;
    }
  }

  if(node[index].l==node[index].r){
    node[index].wl=node[index].wr=node[index].left_end=node[index].right_start=0;
  }else if(node[index].l>=l && node[index].r<=r){
    node[index].lazy=true;
    node[index].lazy_white=false;
    node[index].wl=node[index].wr=node[index].left_end=node[index].right_start=0;
  }else{
    int mid=(node[index].l+node[index].r)/2;
    if(l<=mid && r>=node[index].l) paintBlack(l,r,index<<1);
    if(r>=mid+1 && l<=node[index].r) paintBlack(l,r,index<<1|1);
    update(index);
  }
}

void SegTree::update(int index){
  //cout<<"update:"<<endl;

  int tmp_left,tmp_right;
  int longest=-1;
  tmp_left=node[index<<1|1].wl;
  tmp_right=node[index<<1|1].wr;
  node[index].wl=node[index].wr=0;

  //cout<<"tmp_left="<<tmp_left<<" ,tmp_right="<<tmp_right<<endl<<endl;

  if( tmp_left && tmp_right){
    longest=point[tmp_right]-point[tmp_left]+1;
    node[index].wl=tmp_left;
    node[index].wr=tmp_right;
  }

  tmp_left=node[index<<1].right_start;
  tmp_right=node[index<<1|1].left_end;
  //cout<<"tmp_left="<<tmp_left<<" ,tmp_right="<<tmp_right<<endl<<endl;

  if(tmp_left && tmp_right){
    if( (point[tmp_right]-point[tmp_left]+1) >longest){
      node[index].wl=tmp_left;
      node[index].wr=tmp_right;
      longest=point[tmp_right]-point[tmp_left]+1;
    }
  }

  tmp_left=node[index<<1].wl;
  tmp_right=node[index<<1].wr;
  //cout<<"tmp_left="<<tmp_left<<" ,tmp_right="<<tmp_right<<endl<<endl;

  if( tmp_left && tmp_right){
    if( (point[tmp_right]-point[tmp_left]+1) >longest){
      node[index].wl=tmp_left;
      node[index].wr=tmp_right;
      longest=point[tmp_right]-point[tmp_left]+1;
    }
  }

  //更新left_end
  node[index].left_end=node[index<<1].left_end;
  if(node[index].left_end == node[index<<1].r){
    if(node[index<<1|1].left_end!=0) node[index].left_end=node[index<<1|1].left_end;
  }
  //更新right_start
  node[index].right_start=node[index<<1|1].right_start;
  if(node[index].right_start == node[index<<1|1].l ){
    if(node[index<<1].right_start!=0) node[index].right_start=node[index<<1].right_start;
  }

/*
  cout<<"update finish"<<endl;
  cout<<"index="<<index<<" ,l="<<node[index].l<<" ,r="<<node[index].r<<" ,wl="<<node[index].wl<<" ,wr="<<node[index].wr<<" ,left_end="<<node[index].left_end<<" ,right_start="<<node[index].right_start<<endl;
  cout<<endl;
*/
}


SegTree seg_tree;

int main(){
  while(scanf("%d",&N)!=EOF){
    int p=1;
    for(int i=0;i<N;i++){
      char color;
      scanf("%d%d",&command[i].l,&command[i].r);
      point[p++]=command[i].l;
      point[p++]=command[i].r;
      getchar();
      scanf("%c",&color);
      if(color=='w') command[i].white=true;
      else command[i].white=false;
    }
    sort(point+1,point+p);
/*
    cout<<"排序完成:"<<endl;
    for(int i=1;i<p;i++){
      cout<<point[i]<<"  ";
    }
    cout<<endl<<endl;
*/

    total=remove_dup(point,p);
/*
    cout<<"去重完成:"<<endl;
    for(int i=1;i<=total;i++) cout<<point[i]<<"  ";
    cout<<endl<<endl;
*/

    seg_tree.buildTree(1,total,1);
    for(int i=0;i<N;i++){
      int _left=biSearch(command[i].l);
      int _right=biSearch(command[i].r);
/*
      cout<<"command "<<i<<endl;
      cout<<"l="<<command[i].l<<" ,r="<<command[i].r<<endl;
      cout<<"_left="<<_left<<" ,_right="<<_right<<endl<<endl;
*/
      if(command[i].white) seg_tree.paintWhite(_left,_right,1);
      else seg_tree.paintBlack(_left,_right,1);
    }
    if(seg_tree.node[1].wl && seg_tree.node[1].wr)
      cout<<point[seg_tree.node[1].wl]<<" "<<point[seg_tree.node[1].wr]<<endl;
    else
      cout<<"Oh, my god"<<endl;
  }
}

//在point进行二分查找
int biSearch(int a){
  int l=1,r=total;
  int mid;
  while(l<=r){
    mid=(l+r)/2;
    if(point[mid]>a) r=mid-1;
    else if(point[mid]<a) l=mid+1;
    else return mid;
  }
}

//point[0]~point[_last-1]去重,返回去重以后的数组大小
int remove_dup(int *point,int _last){
  int curr=1,p;
  for(p=2;p<_last;p++){
    if(point[p]!=point[p-1]) point[++curr]=point[p];
  }
  //如果point相邻的两个点不是差1,需要再插入一个点
  tmp[1]=point[1];
  int tmp_point=1;
  for(int i=2;i<=curr;i++){
    if(point[i]>(point[i-1]+1)){
      tmp[++tmp_point]=point[i-1]+1;
      tmp[++tmp_point]=point[i]-1;
    }
    tmp[++tmp_point]=point[i];
  }
  for(int i=1;i<=tmp_point;i++){
    point[i]=tmp[i];
  }
  return tmp_point;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值