poj3667,线段树,区间合并,维护左,右,中区间

题目大意:Hotel有N(1 ≤ N ≤ 50,000)间rooms,并且所有的rooms都是连续排列在同一边,groups需要check in 房间,要求房间的编号为连续的r..r+Di-1并且r是最小的;visitors同样可能check
out,并且他们每次check out都是编号为Xi ..Xi +Di-1
(1 ≤ Xi ≤ N-Di+1)的房间,题目的输入有两种样式:

  1. 1  a     :  groups需要check in  a间编号连续的房间,然后这些房间住进去
  2. 2  a   b : visitors  check out 房间,其中房间编号是 a…a+b-1,这些房间清空

要求对于每次request,输出为groups分配数目为a的房间中编号最小的房间编号

维护几个数据:该区间最大连续长度,从左边第一个开始的最大连续长度lmax,从右边开始的最大连续长度rmax,是否颜色一样(是某个颜色)tong;

然后a[root*2].rmax+a[root*2+1].lmax就是中间部分的长度,一些细节比较难以处理,要注意一下。

这题和POJ3368真的太像了,简直是孪生兄弟。

本题的重点是如何处理中间接起来的问题,当然3368也是。

具体就看代码吧:

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<math.h>
#include<stdlib.h>
using namespace std;
struct pai
{
	int left, right, lmax, rmax, zmax, tong,col;
}a[50000*4+20];

struct PP
{
	int left; int right;
}o;
void up(int root)
{
	int tmax;
	if (a[root * 2].tong==1 && a[root * 2 + 1].tong==1)//说明房间还有,这里很可能有问题,脑子乱
	{
		a[root].tong = 1;
	}
	a[root].rmax = a[root * 2 + 1].rmax;
	a[root].lmax = a[root * 2].lmax;
	if (a[root * 2].rmax && a[root * 2 + 1].lmax)
	{
		if (a[root * 2 + 1].tong==1)
		{
			tmax = a[root * 2 + 1].zmax + a[root * 2].rmax;
			a[root].rmax = tmax;
		}
		if (a[root * 2].tong==1)
		{
			tmax = a[root * 2].zmax + a[root * 2 + 1].lmax;
			a[root].lmax = tmax;
		}
		tmax = a[root * 2].rmax + a[root * 2 + 1].lmax;
	}
	else
	{
		tmax = -1;
	}
	int max1 = a[root * 2].zmax;
	int max2 = a[root * 2 + 1].zmax;

	int max3 = max(max1, max2);
	max3 = max(tmax, max3);
	a[root].zmax = max3;
}
void down(int root)
{
	if (a[root].tong == 0)
	{
		a[root * 2].lmax =a[root*2].rmax=a[root*2].zmax= 0;
		a[root * 2+1].lmax = a[root * 2+1].rmax = a[root * 2+1].zmax = 0;
		a[root * 2].tong = a[root * 2 + 1].tong = 0;
		a[root].tong = 7;
	}
	if (a[root].tong == 1)
	{
		a[root * 2].lmax = a[root * 2].rmax = a[root * 2].zmax = a[root * 2].right - a[root * 2].left + 1;
		a[root * 2 + 1].lmax = a[root * 2 + 1].rmax = a[root * 2 + 1].zmax = a[root * 2+1].right - a[root * 2+1].left + 1;
		a[root * 2].tong = a[root * 2 + 1].tong = 1;
		a[root].tong = 7;
	}
}
void build(int left, int right, int root)
{
	a[root].left = left;
	a[root].right = right;
	if (left == right)
	{
		a[root].lmax = 1;
		a[root].rmax = 1;
		a[root].lmax = 1;
		a[root].zmax = 1;
		a[root].tong = 1;
		a[root].col = 1;
		return;
	}
	int m = (left + right) / 2;
	build(left, m, root * 2);
	build(m + 1, right, root * 2 + 1);
	up(root);
}

int search(int left, int right, int root,int x)
{
	if (a[root].zmax < x)
	{
		return -1;
	}
	if (a[root * 2].lmax >= x)
	{
		o.left = a[root * 2].left;
		o.right = a[root * 2].left + x - 1;
		return o.left;
	}
	if (a[root * 2].zmax >= x)
	{
		return search(a[root * 2].left, a[root * 2].right, root * 2, x);
	}
	if ((a[root * 2].rmax + a[root * 2 + 1].lmax) >= x)
	{
		o.left = a[root * 2].right - a[root * 2].rmax + 1;//这里有可能不对
		o.right = o.left + x - 1;
		return o.left;
	}

	return search(a[root * 2 + 1].left, a[root * 2 + 1].right, root * 2 + 1, x);
}

void change(int left, int right, int root, int x)
{
	if (a[root].left >= left && a[root].right <= right)
	{
		a[root].lmax = a[root].rmax = a[root].zmax=a[root].tong = 0;//tong先没搞
		return;
	}

	down(root);
	int m = (a[root].left + a[root].right) / 2;
	if (left <= m)
	{
		change(left, right, root * 2, x);
	}
	if (right > m)
	{
		change(left,right, root * 2+1, x);
	}
	up(root);

}

void change2(int left, int right, int root, int x)
{
	if (a[root].left >= left && a[root].right <= right)
	{
		a[root].lmax = a[root].rmax = a[root].zmax=a[root].right-a[root].left+1;
		a[root].tong = 1;
		return;
	}

	down(root);
	int m = (a[root].left + a[root].right) / 2;
	if (left <= m)
	{
		change2(left, right, root * 2, x);
	}
	if (right > m)
	{
		change2(left, right, root * 2 + 1, x);
	}
	up(root);
}
int main()
{
	int i,j,n, m,temp,b,c,temp2;
	while (~scanf("%d%d", &n, &m))
	{
		build(1, n, 1);
		for (i = 0; i < m; i++)
		{
			scanf("%d", &temp);

			if (temp == 1)
			{
				scanf("%d", &b);
				temp2=search(1, n, 1, b);
				if (temp2 != -1)
				{
					cout << temp2 << endl;
				}
				else
				{
					cout << '0' << endl;
					continue;
				}

				change(o.left, o.right, 1, 0);
			}
			else
			{
				scanf("%d%d", &b, &c);
				change2(b,b+c-1, 1, 1);
			}
		}

	}

}
时间: 2024-08-09 23:45:10

poj3667,线段树,区间合并,维护左,右,中区间的相关文章

线段树|计蒜客:斑点蛇-区间和

斑点蛇 有一种神奇斑点蛇,蛇如其名,全身都是斑点,斑点数量可以任意改变. 有一天,蒜头君十分的无聊,开始数蛇上的斑点.假设这条蛇的长度是Ncm,蒜头君已经数完开始时蛇身的第icm上有ai个斑点. 现在蒜头君想知道这条斑点蛇的任意区间的蛇身上一共有多少个斑点.这好像是一个很容易的事情,但是这条蛇好像是和蒜头君过不去,总是刻意的改变蛇身上的斑点数量. 于是,蒜头君受不了了,加上蒜头君有密集型恐惧症.聪明又能干的你能帮帮他吗? 输入格式 第一行一个正整数N(N≤50000)表示这条斑点蛇长度为N厘米,

hdu1698 Just a Hook 线段树:成段替换,总区间求和

转载请注明出处:http://blog.csdn.net/u012860063 题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1698 Problem Description In the game of DotA, Pudge's meat hook is actually the most horrible thing for most of the heroes. The hook is made up of several consecut

线段树分裂合并

线段树分裂合并 我先接触的是线段树合并所以先讲线段树合并. 首先,用来合并的线段树必须是动态开点的.线段树合并所做的事就是合并两棵动态开点线段树的信息,对于两棵动态开点线段树,可能会存在一些公共节点,我们所要做的就是合并这些节点的信息,然后把其他节点的信息继承.理清思路之后,剩下的事就是.设初始信息的个数是\(n\),值域是\(m\),对于每一个初始信息一次这样的操作是\(\log m\)的,而每个信息只会被合并一次,所以一般的线段树合并是\(O(n \log m)\)的.非常好理解,直接上模板

SPOJ COT3 Combat on a tree(Trie树、线段树的合并)

题目链接:http://www.spoj.com/problems/COT3/ Alice and Bob are playing a game on a tree of n nodes.Each node is either black or white initially. They take turns to do the following operation:Choose a white node v from the current tree;Color all white node

POJ-3667 线段树区间合并入门题

题意:长度为n的区间,m个操作,一开始都是0 1 x表示求出长度为x的0的连续区间的最左端,并把这个区间变成1 2 x y表示将区间[x,y]变成0 线段树的区间合并第一题: 每次维护左端连续区间长度ls.右端连续区间长度rs,最大连续长度ms 区间合并的注意点主要在push up操作: 每次更新了一段区间之后向上更新,首先,父区间的ls继承左子树的ls,父区间的rs继承右子树的rs 然后就是重点:左右区间合并之后中间部分可能是连续的!!! 所以:如果整个左.右子区间都是“满的”,父区间的ls和

poj3667 线段树 区间合并

1 //Accepted 3728 KB 1079 ms 2 //线段树 区间合并 3 #include <cstdio> 4 #include <cstring> 5 #include <iostream> 6 #include <queue> 7 #include <cmath> 8 #include <algorithm> 9 using namespace std; 10 /** 11 * This is a document

hdoj 2795 Billboard 【线段树 单点更新 + 维护区间最大值】

Billboard Time Limit: 20000/8000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 15719    Accepted Submission(s): 6629 Problem Description At the entrance to the university, there is a huge rectangular billboard of

HDU - 1540 线段树的合并

这个题题意我大概解释一下,就是一开始一条直线,上面的点全是联通的,有三种操作 1.操作D把从左往右第x个村庄摧毁,然后断开两边的联通. 2.询问Q节点相联通的最长长度 3.把最后破坏的村庄重建. 这个其实也是非常典型的线段树区间合并,正好可以学一下. 我们给线段树的结点赋予5个值,l 区间左端点, r 区间右端点, ls从左端点开始的最大连续个数(左连续),rs从右端点开始最大的连续个数,ms这个节点所表示的区间内部,最大的连续个数. 然后我们考虑建树,由于最开始是相通的,因此这些值初始为r-l

poj 3667 Hotel (线段树的合并操作)

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 t

[题解]luogu_P4198_楼房重建(线段树logn合并

前言:最近购买了润滑脂和几个个性键帽,润滑脂用的太多大键稍微有点黏,但是我可以接受,钢丝声基本没有了,虽然没有拆下来卫星轴调试,但是效果还是有的,买了一个原厂高度的空格,好看是好看,用料也挺厚,但是形变非常严重,不过三十块要什么自行车,方向键是个手柄按键三角方块啥的透光键帽,用料也还行,但是字符有很明显的凹陷,看不出来但是摸起来就很明显,而且由于灯在轴的上方导致只有上面一半有光能透出来,但是我也不太在意 线段树update好题,首先把每个点转化为斜率,这样找一个递增序列即可,但是不同点在于这个递