POJ3667 Hotel

  原题传送:http://poj.org/problem?id=3667

  线段树。

  线段树题目做得还是太少了。这道题对新手来说是一道坎,虽然很容易得出思路,但是一细想会觉得挺恶心的,本想参考别人的AC代码,但一看就思密达了。只能静下心来,花几个小时甚至几天来做,还是会有很大收获的。

  这里有一份不错的结题报告:http://bbs.byr.cn/wForum/disparticle.php?boardName=ACM_ICPC&ID=18374&listType=1


  题目大意是有一个旅馆,其中有N个房间,初始化都为空,接下来有M个操作:
  操作1,形式为1 d,表示需要在旅馆中找到连续的d个房间,如果存在多个连续的d个房间,输出连续区间第一个元素最小的房间, 如果不存在连续的d个房间,输出0;
  操作2,形式为2 s d,表示在房间s至s+d-1的顾客退房,即这些房间重新置为空。
  可见题中都是对某个区间进行操作,我们可以很容易想到,这题应该使用线段树。
  线段树建树时间为O(n*logn),每次插入、查询时间为logn,因此全部复杂度为O(nlogn + mlogn),时间足够。

具体做法:设1代表房间为空,0代表房间已住人
线段树的节点存储以下的变量:
  int l, r, c;      //左端点,右端点,线段长度
  int lp, rp, mp;     //lp表示从线段左端点开始的连续1序列的长度,rp表示从线段右端点开始的连续1序列的长度,mp表示 线段中最大的连续1序列长度 
  int rd, md;           //rd代表从线段右端点开始连续1序列的左端点,md表示线段中最大连续1序列的左端点。
  int flag;               //如果为0,表示当前线段所有房间都已住;1表示所有房间都为空,2表示有房间为空,也有非空。

线段树有两种操作,一种是插入操作insert,一种查询操作search
Insert(int l, int r, int k, int f)
其中[l,r]代表我们要进行操作的区间,k当前树节点编号,f=0代表插入(即将空房间置为1),f=1代表删除(即将满房间置为空房 间)。



Insert大概流程如下:

如果遇到节点t[k].l == l && t[k].r == r,则说明当前区间需要进行操作,根据参数f进行相应操作,返回;

如果当前区间全为0或者全为1,则将其子区间也全部置为0或者1;
//进行这个操作,是因为上一次对这个节点进行操作的时候,并没有对其子区间进行相应的操作,而现在我们需要使用子区间,则 先完成上一次应该对子区间进行的操作。

根据l、r和t[k].l,t[k].r的关系,继续递归。

递归完毕,更新当前节点的lp, rp, mp, rd, md
lp和rp的更新都相对简单,要注意mp的更新,除了要考虑t[2*k].rp+t[2*k+1].lp,还需要考虑t[2*k].mp和t[2*k+1].mp,同时相应 更新md和rd。 结束。 


search(int d, int k) //d代表需要的连续1区间长度,k代表当前节点
先看节点k的左连续1区间lp,如果满足,直接返回左端点l;
如果不满足,查找中间连续1区间mp,如果mp==d,则返回记录的起始点md,如果mp < d或者此时已到达叶节点,则返回-1,查找失 败。
仍需要单独考虑mp > d的情况,因为mp记录是当前区间最长连续1区间的长度,并不代表这是我们要查找的答案,因为可能区间里仍 存在长度符合,而起始点更小的连续1区间,因此我们先进入左子树进行查找,如果失败,接着判断中间连续序列t[2*k].rp + t [2*k+1].lp >= d是否成立,成立则返回此序列起始点,如果不成立,进入右子树进行查找,返回结果。 结束。

回到题目,对于每次输入的操作
第一种操作,1 d,我们先进行search操作,如果能够找到长度不小于d的连续1区间,则返回最小的起始点,否则返回-1。如果找到
这样一个区间,起始点为st,则进行插入操作insert(st, st+d-1, 1, 0),输出st;否则,输出0;
第二种操作,2 s d,直接进行插入操作insert(s, s + d – 1, 1, 1)

最后程序跑了600MS,时限是3000MS,说明算法还是比较合理的。

  (这个程序写得各种bug还各种过样例都不知道调试了多久。为什么代码写得这么长,几十次wa试试~)

View Code
  1 #include <stdio.h>
  2 #include <string.h>
  3 #define lson (cur << 1)
  4 #define rson (cur << 1 | 1)
  5 #define N 50010
  6 
  7 struct node
  8 {
  9   int l, r, c;       // 左端点,右端点,线段长度
 10   int lp, rp, mp;    // lp表示从线段左端点开始的连续1序列的长度,rp表示从线段右端点开始的连续1序列的长度,mp表示线段中最大的连续1序列的长度。
 11   int rd, md;        // rd代表从线段右端点开始连续1序列的左端点,md表示线段中最大连续1序列的左端点。
 12   int flag;          // 如果为0,表示当前线段所有房间都已住;1表示所有房间都为空,2表示有房间为空,也有非空。
 13 }tree[N << 2];
 14 
 15 int n, m, lazy[N << 2];
 16 
 17 void pushup(int cur)
 18 {
 19     if(tree[lson].mp == tree[lson].c)  // lp: 表示从线段左端点开始的连续1序列的长度
 20         tree[cur].lp = tree[lson].c + tree[rson].lp;
 21     else
 22         tree[cur].lp = tree[lson].lp;
 23 
 24     if(tree[rson].mp == tree[rson].c)  // rp: 表示从线段右端点开始的连续1序列的长度
 25         tree[cur].rp = tree[rson].c + tree[lson].rp;
 26     else
 27         tree[cur].rp = tree[rson].rp;
 28 
 29     if(tree[rson].mp == tree[rson].c)  // rd: 从线段右端点开始连续1序列的左端点
 30         tree[cur].rd = (tree[lson].rp == 0 ? tree[rson].l : tree[lson].rd);
 31     else
 32         tree[cur].rd = tree[rson].rd;
 33 
 34     int m = 0;
 35     if(tree[lson].mp > m)              // mp: 表示线段中最大的连续1序列的长度
 36         m = tree[lson].mp, tree[cur].md = tree[lson].md;
 37     if(tree[lson].rp + tree[rson].lp > m)
 38         m = tree[lson].rp + tree[rson].lp, tree[cur].md = (tree[lson].rp == 0 ? tree[rson].l : tree[lson].rd);
 39     if(tree[rson].mp > m)
 40         m = tree[rson].mp, tree[cur].md = tree[rson].md;
 41     tree[cur].mp = m;
 42 
 43     if(tree[lson].flag == 2 || tree[rson].flag == 2 || tree[lson].flag != tree[rson].flag)
 44         tree[cur].flag = 2;
 45     else
 46         tree[cur].flag = tree[lson].flag;
 47 }
 48 
 49 void pushdown(int cur)
 50 {
 51     if(lazy[cur] == 0)
 52     {
 53         lazy[lson] = 0;
 54         tree[lson].flag = 0;
 55         tree[lson].lp = tree[lson].rp = tree[lson].mp = 0;
 56         lazy[rson] = 0;
 57         tree[rson].flag = 0;
 58         tree[rson].lp = tree[rson].rp = tree[rson].mp = 0;
 59         lazy[cur] = -1;
 60     }
 61     if(lazy[cur] == 1)
 62     {
 63         lazy[lson] = 1;
 64         tree[lson].flag = 1;
 65         tree[lson].rd = tree[lson].md = tree[lson].l;
 66         tree[lson].lp = tree[lson].rp = tree[lson].mp = tree[lson].r - tree[lson].l + 1;
 67         lazy[rson] = 1;
 68         tree[rson].flag = 1;
 69         tree[rson].rd = tree[rson].md = tree[rson].l;
 70         tree[rson].lp = tree[rson].rp = tree[rson].mp = tree[rson].r - tree[rson].l + 1;
 71         lazy[cur] = -1;
 72     }
 73 }
 74 
 75 void insert(int cur, int l, int r, int f)
 76 {
 77     if(tree[cur].l >= l && tree[cur].r <= r)
 78     {
 79         lazy[cur] = f;
 80         if(f == 1)
 81         {
 82             tree[cur].lp = tree[cur].rp = tree[cur].mp = tree[cur].r - tree[cur].l + 1;
 83             tree[cur].rd = tree[cur].md = tree[cur].l;
 84             tree[cur].flag = 1;
 85         }
 86         else if(f == 0)
 87         {
 88             tree[cur].lp = tree[cur].rp = tree[cur].mp = 0;
 89             tree[cur].flag = 0;
 90         }
 91         return ;
 92     }
 93     pushdown(cur);
 94     int mid = (tree[cur].l + tree[cur].r) >> 1;
 95     if(mid >= l)
 96         insert(lson, l, r, f);
 97     if(mid + 1 <= r)
 98         insert(rson, l, r, f);
 99     pushup(cur);
100 }
101 
102 void search(int cur, int k, int &ans)
103 {
104     // 没有满足条件或已经到了叶子节点,或该区间无空位,失败返回
105     if(tree[cur].flag == 0 || tree[cur].mp < k || tree[cur].l == tree[cur].r)
106         return ;
107 
108     if(tree[cur].lp >= k)    // 左连续1区间lp,如果满足,直接返回左端点l
109     {
110         ans = tree[cur].l;
111         return ;
112     }
113     if(tree[cur].mp == k)    // 中间连续1区间mp==d,则返回记录的起始点md
114     {
115         ans = tree[cur].md;
116         return ;
117     }
118     pushdown(cur);
119     if(tree[cur].mp > k)     // 先进入左子树进行查找,如果失败,接着判断中间连续序列t[2*k].rp + t[2*k+1].lp >= d是否成立,成立则返回此序列起始点,如果不成立,进入右子树进行查找,返回结果
120     {
121         if(tree[lson].mp >= k)  //  左子树满足条件,直接进入
122             search(lson, k, ans);
123         else  if(tree[lson].rp + tree[rson].lp >= k && tree[lson].rp != 0)  // 跨区间进入左区间
124         {
125             ans = tree[lson].rd;
126             return ;
127         }
128         else   // 进入右区间
129             search(rson, k, ans);
130     }
131     pushup(cur);
132 }
133 
134 void build(int cur, int l, int r)
135 {
136     tree[cur].l = l, tree[cur].r = r, tree[cur].c = r - l + 1;
137     lazy[cur] = -1;
138     if(l == r)
139     {
140         tree[cur].lp = 1;
141         tree[cur].rp = 1;
142         tree[cur].mp = 1;
143         tree[cur].flag = 1;
144         tree[cur].rd = tree[cur].md = l;
145         return ;
146     }
147     int mid = (l + r) >> 1;
148     build(lson, l, mid);
149     build(rson, mid + 1, r);
150     pushup(cur);
151 }
152 
153 int main()
154 {
155     int op, a, b, ans;
156     while(scanf("%d%d", &n, &m) != EOF)
157     {
158         build(1, 1, n);
159         while(m --)
160         {
161             scanf("%d", &op);
162             if(op == 1)
163             {
164                 ans = 0;
165                 scanf("%d", &a);
166                 search(1, a, ans);
167                 if(ans)  // !!!
168                     insert(1, ans, ans + a - 1, 0);
169                 printf("%d\n", ans);
170             }
171             else if(op == 2)
172             {
173                 scanf("%d%d", &a, &b);
174                 insert(1, a, a + b - 1, 1);
175             }
176         }
177     }
178     return 0;
179 }

 

 仅以此题纪念今日美好的中秋节

 

 

 

posted @ 2012-09-30 16:20  芒果布丁  阅读(228)  评论(0编辑  收藏  举报