HDU 4553 约会安排 (区间合并)【线段树】

<题目链接>

  寒假来了,又到了小明和女神们约会的季节。 
  小明虽为屌丝级码农,但非常活跃,女神们常常在小明网上的大段发言后热情回复“呵呵”,所以,小明的最爱就是和女神们约会。与此同时,也有很多基友找他开黑,由于数量实在过于巨大,怎么安排时间便成了小明的一大心事。 
  我们已知小明一共有T的空闲时间,期间会有很多女神或者基友来找小明。 
  作为一个操作系统曾经怒考71分的大神,小明想到了一个算法,即“首次适应算法”,根据操作系统课本的描述,就是找一段最靠前的符合要求的连续空间分配给每个请求,由此小明做出了一个决定: 
  当一个基友来找小明时,小明就根据“首次适应算法”来找一段空闲的时间来和基友约好,如果找到,就说“X,let’s fly”(此处,X为开始时间),否则就说“fly with yourself”;
  当女神来找小明时,先使用一次“首次适应算法”,如果没有找到,小明就冒着木叽叽的风险无视所有屌丝基友的约定,再次使用“无视基友首次适应算法”,两次只要有一次找到,就说“X,don’t put my gezi”(此处,X为开始时间),否则就说“wait for me” 
  当然,我们知道小明不是一个节操负无穷的人,如果和女神约会完,还有剩余时间,他还是会和原来约好的基友去dota的。(举个例子:小西(屌丝)和小明约好在1~5这个时间单位段内打dota,这时候,女神来和小明预约长度为3的时间段,那么最终就是1~3小明去和女神约会,搞定后在4~5和小西打dota) 
  小明偶尔也会想要学习新知识,此时小明就会把某一个时间区间的所有已经预定的时间全部清空用来学习并且怒吼“I am the hope of chinese chengxuyuan!!”,不过小明一般都是三分钟热度,再有人来预定的话,小明就会按耐不住寂寞把学习新知识的时间分配出去。

Input

输入第一行为CASE,表示有CASE组测试数据; 
每组数据以两个整数T,N开始,T代表总共的时间,N表示预约请求的个数; 
接着的N行,每行表示一个女神或者基友的预约,“NS QT”代表一个女神来找小明约一段长为QT的时间,“DS QT”则代表一个屌丝的长为QT的请求,当然也有可能是小明想学知识了,“STUDY!! L R”代表清空L~R区间内的所有请求。 

[Technical Specification] 
1. 1 <= CASE <= 30 
2. 1 <= T, N <= 100000 
3. 1 <= QT <= 110000 
4. 1 <= L <= R <=T

Output

对于每一个case,第一行先输出“Case C:”代表是第几个case,然后N行,每行对应一个请求的结果(参照描述)。 
输出样本(可复制此处): 
“X,let's fly”,”fly with yourself”,”X,don't put my gezi”,”wait for me”,”I am the hope of chinese chengxuyuan!!” 
Sample Input

1
5 6
DS 3
NS 2
NS 4
STUDY!! 1 5
DS 4
NS 2

Sample Output

Case 1:

1,let's fly

4,don't put my gezi

wait for me

I am the hope of chinese chengxuyuan!!

1,let's fly

1,don't put my gezi


解题分析:
由于本题要查询连续区间,所以要用到区间合并,每个点维护三个值,该点对应区间的最长连续长度,最长连续前、后缀长度。并且,本题还涉及一个优先级的概念,因此,建立两个线段树,对这两个线段树进行操作,从而达到模拟优先级的作用,具体实现见代码。

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <algorithm>
  4 using namespace std;
  5 
  6 #define Lson rt<<1,l,mid
  7 #define Rson rt<<1|1,mid+1,r
  8 const int M =1e5+5;
  9 int n,q;
 10 int tr[M<<2][2],llen[M<<2][2],rlen[M<<2][2];
 11 int lazy[M<<2][2];
 12 
 13 void Pushup(int rt,int len,int ty){   //区间合并,维护最长连续区间
 14     llen[rt][ty]=llen[rt<<1][ty];
 15     rlen[rt][ty]=rlen[rt<<1|1][ty];
 16     if(llen[rt<<1][ty]==(len-(len>>1)))llen[rt][ty]+=llen[rt<<1|1][ty];
 17     if(rlen[rt<<1|1][ty]==(len>>1))rlen[rt][ty]+=rlen[rt<<1][ty];
 18     tr[rt][ty]=max(max(tr[rt<<1][ty],tr[rt<<1|1][ty]),rlen[rt<<1][ty]+llen[rt<<1|1][ty]);    
 19 }
 20 void Pushdown(int rt,int len,int ty){    //将lazy标记下放
 21     if(lazy[rt][ty]!=-1){
 22         int tmp=lazy[rt][ty];
 23         tr[rt<<1][ty]=llen[rt<<1][ty]=rlen[rt<<1][ty]=(len-(len>>1))*tmp;
 24         tr[rt<<1|1][ty]=llen[rt<<1|1][ty]=rlen[rt<<1|1][ty]=(len>>1)*tmp;
 25         lazy[rt<<1][ty]=lazy[rt<<1|1][ty]=tmp;
 26         lazy[rt][ty]=-1;
 27     }
 28 }
 29 void build(int rt,int l,int r,int ty){
 30     lazy[rt][ty]=-1;   
 31     if(l==r){
 32         tr[rt][ty]=llen[rt][ty]=rlen[rt][ty]=1;   //值为1代表该点有时间,为0代表没时间
 33         return;
 34     }
 35     int mid=(l+r)>>1;
 36     build(Lson,ty);
 37     build(Rson,ty);
 38     Pushup(rt,r-l+1,ty);
 39 }
 40 void update(int rt,int l,int r,int L,int R,int c,int ty){    
 41     if(L<=l&&r<=R){
 42         lazy[rt][ty]=c;
 43         tr[rt][ty]=llen[rt][ty]=rlen[rt][ty]=(r-l+1)*c;
 44         return;
 45     }
 46     Pushdown(rt,r-l+1,ty);
 47     int mid=(l+r)>>1;
 48     if(L<=mid)update(Lson,L,R,c,ty);
 49     if(R>mid)update(Rson,L,R,c,ty);
 50     Pushup(rt,r-l+1,ty);
 51 }
 52 int query(int rt,int l,int r,int len,int ty){   //返回满足连续区间>=len的区间最左值,掌握这种query函数
 53     if(l==r)return l;
 54     Pushdown(rt,r-l+1,ty);
 55     int mid=(l+r)>>1;
 56     //因为要尽量使符合要求的区间靠前,所以这里按左、中、右的顺序来查询
 57     if(tr[rt<<1][ty]>=len)return query(Lson,len,ty);
 58     else if(rlen[rt<<1][ty]+llen[rt<<1|1][ty]>=len)return mid-rlen[rt<<1][ty]+1;   //如果是在左右子区间的后缀、前缀之间,则直接返回左区间的后缀最左值的下标
 59     else return query(Rson,len,ty);
 60 }
 61 int main(){
 62     int T,ncase=0;scanf("%d",&T);
 63     while(T--){
 64         scanf("%d%d",&n,&q);
 65         build(1,1,n,0);  //基友的请求建一个线段树
 66         build(1,1,n,1);  //女神的请求建一颗线段树,更新的时候,根据优先级,对这两个不同的线段树进行操作
 67         char op[10];
 68         int x,y;
 69         printf("Case %d:\n", ++ncase);
 70         while(q--){
 71             scanf("%s",op);
 72             if(op[0]=='D'){
 73                 scanf("%d",&x);
 74                 if(tr[1][0]>=x){   //如果存在>=x的连续区间
 75                     int ans=query(1,1,n,x,0);   //得到符合要求区间的最左值下标
 76                     printf("%d,let's fly\n", ans);
 77                     update(1,1,n,ans,ans+x-1,0,0);   //只更新基友树上的时间安排
 78                 }
 79                 else printf("fly with yourself\n");
 80             }
 81             else if(op[0]=='N'){
 82                 scanf("%d",&x);
 83                 if(tr[1][0]>=x){   //如果不用占用基友的时间也能与女神约会,即如果优先级低的基友树上也能满足
 84                     int ans=query(1,1,n,x,0);
 85                     printf("%d,don't put my gezi\n", ans);
 86                     update(1,1,n,ans,ans+x-1,0,0);  //基友树上要更新一段
 87                     update(1,1,n,ans,ans+x-1,0,1);  //女神树上也要更新同样一段,因为女神的要求之间优先级是相同的,如果有女神占了一段时间,那么其它女神也不能占用
 88                 }
 89                 else{
 90                     if(tr[1][1]>=x){   //只要女神树上能满足,此时即使基友树上发生冲突也不管,如果需要占用基友时间,直接强制更新,体现了女神的高优先级
 91                         int ans=query(1,1,n,x,1);
 92                         printf("%d,don't put my gezi\n", ans);
 93                         update(1,1,n,ans,ans+x-1,0,0);
 94                         update(1,1,n,ans,ans+x-1,0,1);
 95                     }
 96                     else printf("wait for me\n");
 97                 }
 98             }
 99             else{
100                 scanf("%d%d", &x, &y);
101                 printf("I am the hope of chinese chengxuyuan!!\n");
102                 update(1,1,n,x,y,1,0);    //同时将基友树和女神树上的要求全部清除
103                 update(1,1,n,x,y,1,1);
104             }
105         }
106     }
107     return 0;
108 }



2018-10-01
posted @ 2018-10-01 19:58  悠悠呦~  阅读(234)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end