题目链接: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;
}