【POJ 3667 】 线段树之区间操作

题目连接:http://poj.org/problem?id=3667

 

题目大意:让你对一个区间进行操作。输入Q C 或者 Q C D。

Q ==1  输入 C: 表示让你求1-n中是否有连续的C个空hotel,如果有多个连续的C个空hotel,则取最左边的,并输出最左边的第一间hotel标号。让人住进去,那么这些空房就不能住人了。如果不存在连续的C个hotel,则输出 0。

Q==2   输入 C D: 表示 从标号C到C+D-1的hotel的房间全部要退房,那么这么hotel都变成空。

 

解题思路:

用到线段树的区间操作,具体解析见代码。

  1 #include <cstdio>
  2 #include <cmath>
  3 #include <iostream>
  4 #include <algorithm>
  5 using namespace std;
  6 
  7 #define lz 2*u,l,mid
  8 #define rz 2*u+1,mid+1,r
  9 const int maxn=50005;
 10 int  flag[4*maxn];   ///标记
 11 
 12 struct node
 13 {
 14     int lm; ///从左边第一个点开始最长的连续空hotel
 15     int rm; ///以右边最后一个结束的最长的连续空hotel
 16     int sm; ///整段区间最大的连续空hotel 
 17 } tree[4*maxn];
 18 
 19 void push_up(int u, int l, int r)   ///向上更新
 20 {
 21     tree[u].lm=tree[2*u].lm;          
 22     tree[u].rm=tree[2*u+1].rm;
 23     tree[u].sm=max(tree[2*u].sm,tree[2*u+1].sm);
 24     int mid=(l+r)>>1;
 25     if(tree[2*u].lm==mid-l+1) tree[u].lm+=tree[2*u+1].lm;  ///!!这里注意,当左孩子左边连续的达到整个区间时,要加上右孩子的左边区间
 26     if(tree[2*u+1].rm==r-mid) tree[u].rm+=tree[2*u].rm;   ///!!考虑右区间,同上
 27     int t=tree[2*u].rm+tree[2*u+1].lm;
 28     if(t>tree[u].sm) tree[u].sm=t;
 29 }
 30 
 31 void push_down(int u, int l, int r)  ///向下更新
 32 {
 33     if(flag[u]==-1) return ;
 34     if(flag[u])
 35     {
 36         flag[2*u]=flag[2*u+1]=flag[u];
 37         tree[2*u].lm=tree[2*u].rm=tree[2*u].sm=0;
 38         tree[2*u+1].lm=tree[2*u+1].rm=tree[2*u+1].sm=0;
 39         flag[u]=-1;
 40     }
 41     else
 42     {
 43         flag[2*u]=flag[2*u+1]=flag[u];
 44         int mid=(l+r)>>1;
 45         tree[2*u].lm=tree[2*u].rm=tree[2*u].sm=mid-l+1;
 46         tree[2*u+1].lm=tree[2*u+1].rm=tree[2*u+1].sm=r-mid;
 47         flag[u]=-1;
 48     }
 49 }
 50 
 51 void build(int u, int l, int r)  ///建树
 52 {
 53     flag[u]=-1;
 54     if(l==r)
 55     {
 56         tree[u].lm=tree[u].rm=tree[u].sm=1;
 57         return ;
 58     }
 59     int mid=(l+r)>>1;
 60     build(lz);
 61     build(rz);
 62     push_up(u,l,r);
 63 }
 64 
 65 void Update(int u, int l, int r, int tl, int tr, int c)   ///更新操作
 66 {
 67     if(tl<=l&&r<=tr)
 68     {
 69         tree[u].sm=tree[u].lm=tree[u].rm=(c==1?0:r-l+1);
 70         flag[u]= c;  
 71         return ;
 72     }
 73     push_down(u,l,r);   ///再次遇见此段区间时,延迟标记同步向下更新
 74     int mid=(l+r)>>1;
 75     if(tr<=mid) Update(lz,tl,tr,c);
 76     else if(tl>mid) Update(rz,tl,tr,c);
 77     else
 78     {
 79         Update(lz,tl,mid,c);    ///注意区间分隔开,tl,tr跨越两个左右区间
 80         Update(rz,mid+1,tr,c);
 81     }
 82     push_up(u,l,r);     ///递归的时候同步向上更新
 83 }
 84 
 85 int Query(int u, int l, int r, int num)   ///询问操作
 86 {
 87     if(l==r)
 88         return l;
 89     push_down(u,l,r);     ///延迟标记向下传递
 90     int mid=(l+r)>>1;
 91     if(tree[2*u].sm>=num) return Query(lz,num);
 92     else if(tree[2*u].rm+tree[2*u+1].lm>=num&&tree[2*u].rm>=1) return mid-tree[2*u].rm+1;   ///满足条件时,返回左边rm连续的hotel第一个房间标号
 93     else
 94         return Query(rz,num);
 95 }
 96 
 97 int main()
 98 {
 99     int n, m;
100     while(~scanf("%d%d",&n,&m))
101     {
102         build(1,1,n);
103         while(m--)
104         {
105             int p, u, v;
106             scanf("%d",&p);
107             if(p==1)
108             {
109                 scanf("%d",&u);
110                 if(tree[1].sm<u)  ///特判一下是否有这么多个连续的空hotel,没有则直接输出,不用操作
111                 {
112                     puts("0"); continue;
113                 }
114                 int p=Query(1,1,n,u);
115                 printf("%d\n",p);
116                 Update(1,1,n,p,p+u-1,1);
117             }
118             else
119             {
120                 scanf("%d%d",&u,&v);
121                 Update(1,1,n,u,u+v-1,0);
122             }
123         }
124     }
125     return 0;
126 }
原文地址:https://www.cnblogs.com/kane0526/p/2832637.html