線段樹·POJ-3667·Hotel···區間合併

#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;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章