首页 > 程序开发 > 软件开发 > 其他 >

【hdu 2871】Memory Control

2016-09-13

【hdu 2871】Memory Control

Time Limit: 2000/1000 MS (Java/Others)Memory Limit: 32768/32768 K (Java/Others)

Total Submission(s): 6153Accepted Submission(s): 1457

Problem Description Memory units are numbered from 1 up to N.

A sequence of memory units is called a memory block.

The memory control system we consider now has four kinds of operations:

1.Reset Reset all memory units free.

2.New x Allocate a memory block consisted of x continuous free memory units with the least start number

3.Free x Release the memory block which includes unit x

4.Get x Return the start number of the xth memory block(Note that we count the memory blocks allocated from left to right)

Where 1<=x<=N.You are request to find out the output for M operations.

Input Input contains multiple cases.

Each test case starts with two integer N,M(1<=N,M<=50000) ,indicating that there are N units of memory and M operations.

Follow by M lines,each line contains one operation as describe above.

Output For each “Reset” operation, output “Reset Now”.

For each “New” operation, if it&rsquo;s possible to allocate a memory block,

output “New at A”,where Ais the least start number,otherwise output “Reject New”.

For each “Free” operation, if it&rsquo;s possible to find a memory block occupy unit x,

output “Free from A to B”,where A and B refer to the start and end number of the memory block,otherwise output “Reject Free”.

For each “Get” operation, if it&rsquo;s possible to find the xth memory blocks,

output “Get at A”,where A is its start number,otherwise output “Reject Get”.

Output one blank line after each test case.

Sample Input

6 10 New 2 New 5 New 2 New 2 Free 3 Get 1 Get 2 Get 3 Free 3 Reset

Sample Output

New at 1 Reject New New at 3 New at 5 Free from 3 to 4 Get at 1 Get at 5 Reject Get Reject Free Reset Now

【题解】

给你n个空的记忆单位。然后4种操作

1.New X ->在n个记忆单位里面找到连续的X个空单位。且连续空单位的起点尽量靠左。然后占据这X个空单位。

2.Free x ->找到第x个记忆单位所属的连续单位块的编号。把这个连续单位块全部删掉(置0)

3.Reset ->把n个记忆单位全部置空。之前出现过的连续单位块全都不在了。

4.Get X ->从左往右数第X个"连续单位块"的起点坐标是什么。

(如 1 1 0 1 1,下标从1开始,则Get 2应输出Get at 4)

->0表示空的单位

这题要从整体来想。

对于New操作,你所要的X个连续的空单位块有两种情况。

第一种:在这个区间的左半部分。

第二种:在这个区间的右半部分。

第三种:一部分在左半部分,一部分在右半部分。横跨了左右两个子区间。

横跨两个子区间的情况;

如左区间是1 0 1 0 0然后右区间是 0 0 0 1 1

我们要记录两个东西

llx[rt]和rlx[rt];

左边那个区间

llx[rt] = 0;rlx[rt] = 2;

右边那个区间

llx[rt] = 3;rlx[rt] = 0;

看懂了吧

再给个例子

0 0 1 0 1 0

llx[rt] = 2;rlx[rt] = 1

"lx就是lianxu(连续)"

然后再记录一个lx[rt],表示整个区间不管在哪里的最长连续块。

比如左区间是1 0 1 0 0然后右区间是 0 0 0 1 1

那整个区间的lx[rt] =max{ lx[rt<<1] ,lx[rt<<1|1],rlx[rt<<1]+llx[rt<<1|1]};

这样就完成了更新

然后询问New的时候

尽量往左输出就可以了。看代码吧。

染色的时候在懒惰标记上多加一个域,表示这个区间覆盖了什么编号的连续块就好了。

然后有一个比较棘手的就是从左往右数第x个连续块的问题。

这个要用平衡树来求第k小数(平衡树记录的是这个区间的左端点和编号).比较的是左端点。

比如当前节点的左端点都比左子树的任何一个节点的左端点大。懂了吧。

Free的时候要加一个删除操作。有点麻烦的。然后还要写K小。

看代码吧。

我平衡树直接拿之前打的一个SBT改了一下。

【代码】

#include 
#include 
#include 
#define lson begin,m,rt<<1
#define rson m+1,end,rt<<1|1

using namespace std;

const int MAXN = 50101;

struct biaoji
{
	int cover, bianhao;
};

struct data2
{
	int z, y;
};

data2 a[MAXN * 2];

int n, m, lx[MAXN * 4], totnum = 0,totn = 0, llx[MAXN * 4], rlx[MAXN * 4],root;
int l[MAXN * 3], r[MAXN * 3],si_ze[MAXN*3],key[MAXN*3],bianhao[MAXN*3];
biaoji lazy_tag[MAXN * 4];

void right_rotation(int &t) // 右旋代码。  
{
	int k = l[t];//先将根节点的左儿子提取出来。  
	l[t] = r[k];//然后把根节点的左儿子的右子树接在根节点的左子树位置  
	r[k] = t;//然后把原来的根节点的左儿子往右上旋转代替原来的根节点成为新的根节点。  
	si_ze[k] = si_ze[t];//新的根节点等于原来的根节点的大小  
	si_ze[t] = si_ze[l[t]] + si_ze[r[t]] + 1;//l[t]就是被转移过来的r[k(原来的)]。然后t的右子树是没有发生变化的。  
	t = k;//新的根节点变成了t。  
}

void left_rotation(int &t)//左旋代码  
{
	int k = r[t]; //先获取根节点的右儿子。  
	r[t] = l[k];//然后用根节点的右儿子的左子树来代替根节点的右子树;  
	l[k] = t;//然后把根节点的右儿子往左上的方向提上来代替原来的根节点  
	si_ze[k] = si_ze[t];//k成为了新的根节点,它的大小和原来的根节点是一样的。  
	si_ze[t] = si_ze[r[t]] + si_ze[l[t]] + 1;//t的左子树没有发生变化,然后右子树已经修改过了。直接加上相应的大小再加上自身即可  
	t = k;//根节点发生了变化变成了k  
}

void maintain(int &t, bool flag)//maintain函数,看SBT树的实现的话绕道。这里只写一下大概思路  
{
	if (flag) //往左调整  
	{
		if (si_ze[l[l[t]]] > si_ze[r[t]])//这是/型的情况  
			right_rotation(t);
		else
			if (si_ze[r[l[t]]] > si_ze[r[t]])//这是<型的情况  
			{
				left_rotation(l[t]);
				right_rotation(t);
			}
			else
				return;//如果都不是的话就结束递归  
	}
	else
	{
		if (si_ze[r[r[t]]] > si_ze[l[t]])//这是\型的情况  
		{
			left_rotation(t);
		}
		else
			if (si_ze[l[r[t]]] > si_ze[l[t]])//这是>型的情况。  
			{
				right_rotation(r[t]);
				left_rotation(t);
			}
			else
				return;
	}
	maintain(l[t], true);//可以这样理解,如果是\型,原来的根节点的右子树变成了原来的根节点的右儿子的左子树  
						 //而原来的根节点的左子树不变。  
						 //那么是原来的左子树比较大还是新的右子树比较大呢?  
						 //当然是原来的左子树比较大,所以原来的根节点变成根节点的左儿子之后,调整的话应该是/型或<型的应该往左  
	maintain(r[t], false);//往右的同理。  
	maintain(t, true);//这两句是因为左右子树如果都发生了变化,需要重新维护一下根节点的子树。  
	maintain(t, false);//既然不知道方向,就两个方向都试试  
}

void insert(int &t, int data,int bian_hao)//把data这个元素插入到平衡树中。  
{
	if (t == 0)//如果是一个之前未到达过的节点则直接新创建一个节点  
	{
		t = ++totnum;
		l[t] = r[t] = 0;//把这个data记录在这个位置  
		si_ze[t] = 1;//这个节点的大小为1  
		key[t] = data;
		bianhao[t] = bian_hao;
	}
	else
	{
		si_ze[t]++;//否则就判断要往哪里走,不论往哪里走,以t为根节点的树的大小肯定递增了  
		if (data k)//如果比它小的数+1大于了k,那么就继续往左找第k小数  
			return k_th(l[t], k);
		else
			if (si_ze[l[t]] + 1> 1))) //如果左边都是空的
		llx[rt] += llx[rt << 1 | 1];//可以加上右边的左边那部分
	rlx[rt] = rlx[rt << 1 | 1];
	if (rlx[rt] == len >> 1)
		rlx[rt] += rlx[rt << 1];
}

void build(int begin, int end, int rt)
{
	lazy_tag[rt].cover = 0;
	if (begin == end)
	{
		lx[rt] = llx[rt] = rlx[rt] = 1;
		return;
	}
	int m = (begin + end) >> 1;
	build(lson);
	build(rson);
	push_up(rt, end - begin + 1);
}

void init()
{
	memset(l, 0, sizeof(l));
	memset(si_ze, 0, sizeof(si_ze));
	memset(r, 0, sizeof(r));
	totn = 0;
	totnum = 0;
	root = 0;
	memset(lazy_tag, 255, sizeof(lazy_tag));
}

void push_down(int rt, int len)
{
	if (lazy_tag[rt].cover != -1)
	{
		lazy_tag[rt << 1].cover = lazy_tag[rt << 1 | 1].cover = lazy_tag[rt].cover;
		if (lazy_tag[rt].cover == 1)
		{
			llx[rt << 1] = rlx[rt << 1] = lx[rt << 1] = 0;
			llx[rt << 1 | 1] = rlx[rt << 1 | 1] = lx[rt << 1 | 1] = 0;
			lazy_tag[rt << 1].bianhao = lazy_tag[rt << 1 | 1].bianhao = lazy_tag[rt].bianhao;
			//如果是覆盖操作要往下传递编号。
		}
		else
		{
			llx[rt << 1] = rlx[rt << 1] = lx[rt << 1] = len - (len >> 1);
			llx[rt << 1 | 1] = rlx[rt << 1 | 1] = lx[rt << 1 | 1] = len >> 1;
		}
		lazy_tag[rt].cover = -1;
	}
}

int query(int len, int begin, int end, int rt) 
{
	if (begin == end)
		return begin;
	push_down(rt, end - begin + 1);
	int m = (begin + end) >> 1;
	if (lx[rt << 1] >= len) //要按左边->中间->右边,不然无法满足最左
		return query(len, lson);
	else//横跨的情况如果满足就可以直接返回它的最左坐标了
		if (rlx[rt << 1] + llx[rt << 1 | 1] >= len)
			return m - rlx[rt << 1] + 1;
		else
			return query(len, rson);
}

void up_data(int l, int r, int num, int begin, int end, int rt)
{
	if (l <= begin && end <= r)
	{
		if (num == 1)
		{
			lazy_tag[rt].cover = 1;
			lazy_tag[rt].bianhao = totn; //给这个区间编号
			lx[rt] = llx[rt] = rlx[rt] = 0;
		}
		else
		{
			lazy_tag[rt].cover = 0;
			lx[rt] = llx[rt] = rlx[rt] = end - begin + 1;
		}
		return;
	}
	push_down(rt, end - begin + 1);
	int m = (begin + end) >> 1;
	if (l <= m)
		up_data(l, r, num, lson);
	if (m < r)
		up_data(l, r, num, rson);
	push_up(rt, end - begin + 1);
}

int find(int k, int begin, int end, int rt) //寻找第k个记忆单位所属的连续记忆块的编号
{
	if (begin == end)
		if (lazy_tag[rt].cover == 0)
			return 0;
		else
			return lazy_tag[rt].bianhao;
	push_down(rt, end - begin + 1);
	int m = (begin + end) >> 1;
	if (k <= m)
		return find(k, lson);
	else
		return find(k, rson);
}

void output_ans()
{
	for (int i = 1; i <= m; i++)
	{
		char op[10];
		scanf("%s", op);
		if (op[0] == &#39;N&#39;)
		{
			int size;
			scanf("%d", &size);
			if (lx[1] < size)
				printf("Reject New\n");
			else
			{
				totn++;
				int p = query(size, 1, n, 1);
				printf("New at %d\n", p);
				up_data(p, p + size - 1, 1, 1, n, 1);
				a[totn].z = p;
				a[totn].y = p + size - 1;
				insert(root, a[totn].z, totn);
				//把一个左端点为p,编号为totn的节点加入平衡树中。
			}
		}
		else
			if (op[0] == &#39;F&#39;)
			{
				int pos;
				scanf("%d", &pos);
				int num = find(pos, 1, n, 1);
				if (num == 0)
					printf("Reject Free\n");
				else
				{
					printf("Free from %d to %d\n", a[num].z,a[num].y);
					up_data(a[num].z, a[num].y, 0, 1, n, 1);
					de_lete(a[num].z, root);
					//删除平衡树中的左节点为z的那个玩意。
				}
			}
			else
				if (op[0] == &#39;R&#39;)
				{
					printf("Reset Now\n");
					up_data(1, n, 0, 1, n, 1);
					root = 0;//把根节点改为0就能重置这个平衡树了	
				}
				else
					if (op[0] == &#39;G&#39;)
					{
						int k;
						scanf("%d", &k);
						if (si_ze[root] < k)
							printf("Reject Get\n");
						else
						{
							int num = k_th(root, k);
							printf("Get at %d\n", a[num].z);
						}
						
						//快速检索第k个blocks;
						//用平衡树?
						//平衡树的节点记录这个节点是哪个编号的
						//记录它的左端点(作为key值)
						//相当于求第k小数。
					}
	}
}

int main()
{
	//freopen("F:\\rush.txt", "r", stdin);
	//freopen("F:\\rush_out.txt", "w", stdout);
	while (scanf("%d%d", &n, &m)!=EOF)
	{
		init();
		build(1, n, 1);
		output_ans();
		puts("");
	}
	return 0;
}
相关文章
最新文章
热点推荐