POJ 3667 线段树的区间合并简单问题

题目大意:
有一排标号1-N的房间。
操作一:询问是不是有连续长度为a的空房间,有的话住进最左边(占用a个房间)
操作二:将[a,a+b-1]的房间清空(腾出b个房间)
思路:记录每个区间中“靠左”“靠右”“中间”的空房间
线段树操作:update:区间替换
query:询问满足条件的最左端点

题目链接:

http://vjudge.net/problem/viewProblem.action?id=10354

题目操作:

因为这里找从最左边住起的房间,所以这里不能像常规地写query函数那样写了,终于发现自己只会套模板的下场的悲哀了,智商拙计啊T T

而且你在query函数,因为要尽可能找左边的房间,所以要从左边先递归

1.

int query(int cur,int x,int y,int w)
{
    int mid=(x+y)>>1,ls=cur<<1,rs=cur<<1|1;
    if(x==y) return x;  //这里是表示找到树的底层无叶节点了就结束函数,同时也防止找不到点,其实这里如果找不到所求点的话,会不断进入右子树最后抵达最大的子节点返回
    pushdown(cur,x,y);
    if(mc[cur<<1]>=w) return query(ls,x,mid,w);  //如果左侧能找到满足的点,我们不断朝左侧进行递归
    else if(rc[cur<<1]+lc[cur<<1|1]>=w) return mid-rc[cur<<1]+1;//左侧不成立我们在开始找中间并在一起形成的房间的最左侧点
    return query(rs,mid+1,y,w);  //左侧中间都不成立,那么只能进入右子树进行找点
}
因为这个query函数必然有返回值,所以,我们在主函数中必须先进行判断能否找到适合的一连串的房间,然后再进行ans=query(1,1,n,w)操作以及后面的房间入住的覆盖操作。

这个判断很简单 1为线段树根节点,所以mc[1]其实是最大的连续最长房间,if(mc[1]>=w)这个判断执行就可以了

这里介绍一下puts("0"):
puts()函数用来向标准输出设备(屏幕)写字符串并换行,其调用方式为,puts(s);其中s为字符串字符(字符串数组名或字符串指针)。

所以这道题puts("0");就输出了不存在的情况

2.

当然我也可以换种方式在query的函数里面进行一下小小的修改,使得它在不能找到房间的时候输出-1;

int query(int cur,int x,int y,int w)
{
    int mid=(x+y)>>1,ls=cur<<1,rs=cur<<1|1;
    if(x==y) return mc[cur]<w?-1:x; //在这修改一下,然后直接在main函数中判断为-1,那么输出0即可
    pushdown(cur,x,y);
    if(mc[cur<<1]>=w) return query(ls,x,mid,w);
    else if(rc[cur<<1]+lc[cur<<1|1]>=w) return mid-rc[cur<<1]+1;
    return query(rs,mid+1,y,w);
}

总代码如下:

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 using namespace std;
 5 #define N 50010
 6 #define L ls,x,mid
 7 #define R rs,mid+1,y
 8 int rev[N<<2],lc[N<<2],rc[N<<2],mc[N<<2];
 9 void update(int cur,int x,int y)
10 {
11     int mid=(x+y)/2,ls=cur<<1,rs=cur<<1|1;
12     lc[cur]=lc[ls],rc[cur]=rc[rs];
13     mc[cur]=max(mc[ls],mc[rs]);
14     mc[cur]=max(mc[cur],rc[ls]+lc[rs]);
15     if(lc[ls]==mid-x+1) lc[cur]=lc[ls]+lc[rs];
16     if(rc[rs]==y-mid) rc[cur]=rc[rs]+rc[ls];
17 }
18 void build(int cur,int x,int y)
19 {
20     int mid=(x+y)/2,ls=cur<<1,rs=cur<<1|1;
21     rev[cur]=-1;
22     if(x==y){
23         lc[cur]=rc[cur]=mc[cur]=1;
24         return;
25     }
26     build(ls,x,mid);
27     build(rs,mid+1,y);
28     update(cur,x,y);
29 }
30 void pushdown(int cur,int x,int y)
31 {
32     int mid=(x+y)/2,ls=cur<<1,rs=cur<<1|1;
33     if(rev[cur]!=-1){
34         if(rev[cur]==1){
35             rev[ls]=rev[rs]=1;
36             lc[ls]=rc[ls]=mc[ls]=lc[rs]=rc[rs]=mc[rs]=0;
37             rev[cur]=-1;
38         }
39         else if(rev[cur]==0){
40             rev[ls]=rev[rs]=0;
41             lc[ls]=rc[ls]=mc[ls]=mid-x+1;
42             lc[rs]=rc[rs]=mc[rs]=y-mid;
43             rev[cur]=-1;
44         }
45     }
46 }
47 void change(int cur,int x,int y,int s,int t,int v)
48 {
49     int mid=(x+y)/2,ls=cur<<1,rs=cur<<1|1;
50     if(x>=s&&y<=t){
51         rev[cur]=v;
52         lc[cur]=rc[cur]=mc[cur]=v?0:y-x+1;
53         return;
54     }
55     pushdown(cur,x,y);
56     if(mid>=s) change(ls,x,mid,s,t,v);
57     if(mid+1<=t) change(rs,mid+1,y,s,t,v);
58     update(cur,x,y);
59 }
60 int query(int cur,int x,int y,int w)
61 {
62     int mid=(x+y)>>1,ls=cur<<1,rs=cur<<1|1;
63     if(x==y) return x;
64     pushdown(cur,x,y);
65     if(mc[cur<<1]>=w) return query(ls,x,mid,w);
66     else if(rc[cur<<1]+lc[cur<<1|1]>=w) return mid-rc[cur<<1]+1;
67     return query(rs,mid+1,y,w);
68 }
69 int main()
70 {
71     int n,m,op,a,b;
72     while(scanf("%d%d",&n,&m)!=EOF){
73         build(1,1,n);
74         for(int i=0;i<m;i++){
75             scanf("%d",&op);
76             if(op==1){
77                 scanf("%d",&a);
78                 if(mc[1]<a) puts("0");
79                 else{
80                     int ans=query(1,1,n,a);
81                     printf("%d\n",ans);
82                     change(1,1,n,ans,ans+a-1,1);
83                 }
84             }
85             else{
86                 scanf("%d%d",&a,&b);
87                 change(1,1,n,a,a+b-1,0);
88             }
89         }
90     }
91     return 0;
92 }

POJ 3667 线段树的区间合并简单问题

时间: 2024-10-02 21:53:14

POJ 3667 线段树的区间合并简单问题的相关文章

HDU 1540 &amp;&amp; POJ 2892 Tunnel Warfare (线段树,区间合并).

~~~~ 第一次遇到线段树合并的题,又被律爷教做人.TAT. ~~~~ 线段树的题意都很好理解吧.. 题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1540 http://poj.org/problem?id=2892 ~~~~ 我的代码:200ms #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #defin

poj 2777 线段树的区间更新

Count Color Time Limit: 1000 MS Memory Limit: 65536 KB 64-bit integer IO format: %I64d , %I64u Java class name: Main [Submit] [Status] [Discuss] Description Chosen Problem Solving and Program design as an optional course, you are required to solve al

HDU 3308 线段树(区间合并)

LCIS Time Limit: 6000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 3896    Accepted Submission(s): 1766 Problem Description Given n integers.You have two operations:U A B: replace the Ath number by B. (index

Codeforces Round #222 (Div. 1) D. Developing Game 线段树有效区间合并

D. Developing Game Pavel is going to make a game of his dream. However, he knows that he can't make it on his own so he founded a development company and hired n workers of staff. Now he wants to pick n workers from the staff who will be directly res

poj 3667(线段树 区间合并) 开房吧

http://poj.org/problem?id=3667 宾馆有n个房间编号1到n都为空房,然后m个询问,当输入第一个为1的时候,代表要住进x个连续的房间,输入房间号最小的数,如果没有 输出0.当第一个数为2的时候,将从x号到y号的房间又变为空房,没有输出 与区间有关想想用线段树可不可以解决,就像是涂颜色一样把住与未住的房间号做个标记就行,问题是这里给的是区间的长度,并不是 具体的区间,处理起来比较麻烦 还要去找合适的区间,所以引入了len1表示某区间可用的最大长度,len2表示从区间左端开

POJ 3667 线段树区间合并

http://www.cnblogs.com/scau20110726/archive/2013/05/07/3065418.html 用线段树,首先要定义好线段树的节点信息,一般看到一个问题,很难很快能确定线段树要记录的信息做线段树不能为了做题而做,首先线段树是一种辅助结构,它是为问题而生的,因而必须具体问题具体分析回忆一下RMQ问题,其实解决RMQ有很多方法,根本不需要用到线段树,用线段树解决RMQ,其实是利用线段树的性质来辅助解决这个问题回忆一下求矩形面积并或周长并的问题,一般使用的是扫描

【POJ】3667-Hotel(线段树的区间合并)

标记线段树的时候利用 lsum rsum msum 记录最左边 zui右边 以及整个区间的最大连续空位的值 维护的时候注意合并 #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define lson (pos<<1) #define rson (pos<<1|1) const int maxn = 55555; int n,m; struct

HDU_3308_线段树_区间合并

LCIS Time Limit: 6000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 6166    Accepted Submission(s): 2675 Problem Description Given n integers.You have two operations:U A B: replace the Ath number by B. (index

POJ--2892--Tunnel Warfare【线段树】区间合并

链接:http://poj.org/problem?id=2892 题意:有n个村庄排成一排,三种操作: 1. D x 摧毁村庄x 2. Q x 询问村庄x的最长一段没有被摧毁的村庄数量 3. R   恢复上一个被摧毁的村庄 思路:线段树区间合并,lsum记录当前节点往左的最长连续距离,rsum记录当前节点往右的最长连续距离. #include<cstring> #include<string> #include<fstream> #include<iostrea