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

题目传送:Hotel

思路:线段树,区间合并,区间替换,查询最左断点,看胡浩版本的线段树好几天了,今天看这个看了好久,慢慢来吧,具体都写在注释里了

AC代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <map>
#include <set>
#include <deque>
#include <cctype>
#define LL long long
#define INF 0x7fffffff
using namespace std;

int n, m;
const int maxn = 50005;
int sum[maxn << 2];//用于保存当前结点所代表区间的最长连续空房间
int lsum[maxn << 2];//用于保存包含当前结点最左房间在内的一段最长连续空房间
int rsum[maxn << 2];//用于保存包含当前结点最右房间在内的一段最长连续空房间
int lazy[maxn << 2];//延迟标记 

void build(int l, int r, int rt) {//建树
	sum[rt] = lsum[rt] = rsum[rt] = r - l + 1;//初始的时候都是空房间
	lazy[rt] = -1;
	if(l == r) return;
	int mid = (l + r) >> 1;
	build(l, mid, rt << 1);
	build(mid + 1, r, rt << 1 | 1);
}

void pushdown(int rt, int m) {//往下更新
	if(lazy[rt] != -1) {//需要往下更新
		lazy[rt << 1] = lazy[rt << 1 | 1] = lazy[rt];

		//把这段房间置为空或置为住满
		lsum[rt << 1] = rsum[rt << 1] = sum[rt << 1] = lazy[rt] ? 0 : m - (m >> 1);
		lsum[rt << 1 | 1] = rsum[rt << 1 | 1] = sum[rt << 1 | 1] = lazy[rt] ? 0 : (m >> 1);
		lazy[rt] = -1;
	}
}

void pushup(int rt, int m) {//往上更新
	lsum[rt] = lsum[rt << 1];//父节点的左区间先赋值为左孩子的左区间
	rsum[rt] = rsum[rt << 1 | 1];//父节点的右区间先赋值为右孩子的右区间
	if(lsum[rt] == m - (m >> 1)) lsum[rt] += lsum[rt << 1 | 1];//如果左区间满了则继续往右扩张
	if(rsum[rt] == (m >> 1)) rsum[rt] += rsum[rt << 1];//如果右区间满了则继续往左扩张 

	//父节点的最大连续空房间取决于左区间,右区间和中间那个区间的最大值
	sum[rt] = max(rsum[rt << 1] + lsum[rt << 1 | 1], max(sum[rt << 1], sum[rt << 1 | 1]));
}

void update(int L, int R, int c, int l, int r, int rt) {//更新
	if(L <= l && r <= R) {
		sum[rt] = lsum[rt] = rsum[rt] = c ? 0 : r - l + 1;
		lazy[rt] = c;
		return;
	}
	pushdown(rt, r - l + 1);
	int mid = (l + r) >> 1;
	if(L <= mid) update(L, R, c, l, mid, rt << 1);
	if(mid < R) update(L, R, c, mid + 1, r, rt << 1 | 1);
	pushup(rt, r - l + 1);
}

int query(int w, int l, int r, int rt) {//查询
	if(l == r) {
		return l;
	}
	pushdown(rt, r - l + 1);
	int mid = (l + r) >> 1;
	if(sum[rt << 1] >= w) return query(w, l, mid, rt << 1);
	else if(rsum[rt << 1] + lsum[rt << 1 | 1] >= w) {
		return mid - rsum[rt << 1] + 1;//分而治之的思想关键,这里用于求出房间数大于1的所有答案
	}
	else return query(w, mid + 1, r, rt << 1 | 1);
}

int main() {
	scanf("%d %d", &n, &m);
	build(1, n, 1);//建树
	for(int i = 0; i < m; i ++) {
		int op, a, b;
		scanf("%d", &op);
		if(op == 1) {
			scanf("%d", &a);
			if(sum[1] < a) puts("0");
			else {
				int pos = query(a, 1, n, 1);
				printf("%d\n", pos);
				update(pos, pos + a - 1, 1, 1, n, 1);
			}
		}
		else {
			scanf("%d %d", &a, &b);
			update(a, a + b - 1, 0, 1, n, 1);
		}
	}
	return 0;
}
时间: 2024-07-28 20:55:57

POJ - 3667 - Hotel (线段树 - 区间合并)的相关文章

POJ 3667 Hotel(线段树区间合并)

Description The cows are journeying north to Thunder Bay in Canada to gain cultural enrichment and enjoy a vacation on the sunny shores of Lake Superior. Bessie, ever the competent travel agent, has named the Bullmoose Hotel on famed Cumberland Stree

POJ 3667 Hotel ( 线段树区间合并 )

题目链接~~> 做题感悟:这题是接触线段树区间合并的第一题,做的很纠结. 解题思路: 注意线段树上节点代表的信息 : 每个节点需要维护 lc , rc , mc ,add ,见下图: add 为懒惰标记.假设 i 代表父亲节点编号,左儿子为  i * 2  ,右儿子为 i * 2  + 1 ,那么我们可以得到 : T [ i ] .lc 首先加上左儿子的左边的空格数,然后需要判断一下,如果左儿子的左节点占满了整个左区间时需要再加上右儿子的左边的空格数.同理 T [ i ] .rc 也可以这样得到

POJ 3667 Hotel 线段树 区间合并

题意: 1 输入a:询问是不是有连续长度为a的空房间,有的话住进最左边 2 输入a b:将[a,a+b-1]的房间清空 思路:记录区间中最长的空房间 线段树操作: update:区间替换 query:询问满足条件的最左端点 #include <cstdio> #include <iostream> #include <algorithm> #define lson l, m, rt<<1 #define rson m+1, r, rt<<1|1

POJ 3667(线段树区间合并)

http://poj.org/problem?id=3667 题意:两个操作 : 1 选出靠左的长度为a的区间. 2 把从 a到a+b的区间清空. 线段树区间合并+lazy // by caonima // hehe #include <cstdio> #include <cstring> #include <algorithm> #include <vector> #include <cmath> using namespace std; co

poj 3667 Hotel - 线段树

The cows are journeying north to Thunder Bay in Canada to gain cultural enrichment and enjoy a vacation on the sunny shores of Lake Superior. Bessie, ever the competent travel agent, has named the Bullmoose Hotel on famed Cumberland Street as their v

POJ 3667 Hotel 【线段树 区间合并 + Lazy-tag】

Hotel Time Limit: 3000MS Memory Limit: 65536K 链接:POJ 3667   Description The cows are journeying north to ThunderBay in Canada to gain cultural enrichment and enjoy a vacation on the sunnyshores of Lake Superior. Bessie, ever the competent travel agen

线段树(区间合并) POJ 3667 Hotel

题目传送门 1 /* 2 题意:输入 1 a:询问是不是有连续长度为a的空房间,有的话住进最左边 3 输入 2 a b:将[a,a+b-1]的房间清空 4 线段树(区间合并):lsum[]统计从左端点起最长连续空房间数,rsum[]类似,sum[]统计区间最长连续的空房间数, 5 它有三种情况:1.纯粹是左端点起的房间数:2.纯粹是右端点的房间数:3.当从左(右)房间起都连续时,加上另一个子节点 6 从左(右)房间起的数,sum[]再求最大值更新维护.理解没错,表达能力不足 7 详细解释:htt

Poj 3667——hotel——————【线段树区间合并】

Hotel Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 13124   Accepted: 5664 Description The cows are journeying north to Thunder Bay in Canada to gain cultural enrichment and enjoy a vacation on the sunny shores of Lake Superior. Bessie

POJ 3667 Hotel (线段树区间合并 )

Language: Default Hotel Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 12417   Accepted: 5346 Description The cows are journeying north to Thunder Bay in Canada to gain cultural enrichment and enjoy a vacation on the sunny shores of Lak

POJ 3667 Hotel (初遇线段树区间合并)

题意: 有一个线段,从1到n,下面m个操作,操作分两个类型,以1开头的是查询操作,以2开头的是更新操作 1 w 表示在总区间内查询一个长度为w的可用区间并且要最靠左,能找到的话返回这个区间的左端点并占用了这个区间,找不到返回0 2 a len , 表示从单位a开始,清除一段长度为len的区间(将其变为可用,不被占用),不需要输出. 思路: 这是第一次遇到线段树区间合并的题目,写下感悟,还是对线段的更新和查询工作,但是查询的对象的性质已经不像单点那样,查询的是某个线段的最大可用区间是多少,还要一并