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试试~)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
仅以此题纪念今日美好的中秋节