zoukankan      html  css  js  c++  java
  • 【23.68%】【hdu 2871】Memory Control

    Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
    Total Submission(s): 6153    Accepted 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’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’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’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
     

    Source
     

    【题解】

    给你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 <cstdio>
    #include <algorithm>
    #include <cstring>
    #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<key[t])//如果小于它就往左走  
    			insert(l[t], data,bian_hao);
    		else//否则往右走  
    			insert(r[t], data,bian_hao);
    		maintain(t, data<key[t]);//尝试调整平衡树,那个比较的表达式可以确定调整的方向。  
    	}
    }
    
    int k_th(int t, int k) //寻找k小数  
    {
    	if (si_ze[l[t]] + 1 == k)//如果比key[t]小的数的数目+1就是k,那么这个key[t]就是所求的数  
    		return bianhao[t];
    	else
    		if (si_ze[l[t]] + 1 > k)//如果比它小的数+1大于了k,那么就继续往左找第k小数  
    			return k_th(l[t], k);
    		else
    			if (si_ze[l[t]] + 1<k)//如果比它小的数+1小于k,那么问题转换成往右找第(k-(si_ze[l[t]]+1))小的数。  
    				return k_th(r[t], k - (si_ze[l[t]] + 1));
    }
    
    void de_lete(int data, int &t) //这是删除平衡树中某个节点的过程;  
    {
    	si_ze[t]--;//因为这个节点肯定在以t为根的树下面。所以删除后,它的大小会递减。  
    	if (key[t] == data)//如果找到了要删除的元素。  
    	{
    		if (l[t] == 0 && r[t] == 0)//如果它没有左子树或右子树  
    			t = 0;//那么就直接把这个节点置空  
    		else
    			if (l[t] == 0 && r[t] != 0)//如果左子树为空,右子树不为空  
    				t = r[t];//就把这个节点去掉,用右子树来接在下面。  
    			else
    				if (l[t] != 0 && r[t] == 0)//如果左子树不为空,右子树为空  
    					t = l[t];//则直接把这个节点去掉,把左子树接在下面。  
    				else
    					if (l[t] != 0 && r[t] != 0)//如果左右子树都不为空。  
    					{
    						int temp = r[t];//先记录这个节点的右儿子  
    						while (l[temp]) temp = l[temp];//然后这个右儿子不断地往下找左儿子。  
    													   //这样可以找到一个合适的key放在t的位置。  
    													   //它满足<=r[t],且一定小于以r[t]为根的子树中的所有节点。满足平衡树定义。  
    						key[t] = key[temp];//直接把那个合适的key值赋值到这个位置。
    						bianhao[t] = bianhao[temp];
    						de_lete(key[temp], r[t]);//然后调用递归,把那个合适的key值所在的位置的节点去掉。  
    												 //不能单纯就去掉这个节点,因为其可能有右子树。另外,你一直往左下走,实际上没有递减  
    												 //si_ze的值。。所以要重新从r[t]开始往下走。  
    					}
    	}
    	else
    		if (data<key[t])//如果要删除的值小于这个节点的key值,则往左走  
    			de_lete(data, l[t]);
    		else//否则往右走。  
    			de_lete(data, r[t]);
    }
    
    void push_up(int rt, int len)
    {
    	lx[rt] = max(lx[rt << 1], lx[rt << 1 | 1]);
    	lx[rt] = max(lx[rt], rlx[rt << 1] + llx[rt << 1 | 1]);
    	llx[rt] = llx[rt << 1];
    	if (llx[rt] == (len - (len >> 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] == 'N')
    		{
    			int size;
    			scanf("%d", &size);
    			if (lx[1] < size)
    				printf("Reject New
    ");
    			else
    			{
    				totn++;
    				int p = query(size, 1, n, 1);
    				printf("New at %d
    ", 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] == 'F')
    			{
    				int pos;
    				scanf("%d", &pos);
    				int num = find(pos, 1, n, 1);
    				if (num == 0)
    					printf("Reject Free
    ");
    				else
    				{
    					printf("Free from %d to %d
    ", 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] == 'R')
    				{
    					printf("Reset Now
    ");
    					up_data(1, n, 0, 1, n, 1);
    					root = 0;//把根节点改为0就能重置这个平衡树了	
    				}
    				else
    					if (op[0] == 'G')
    					{
    						int k;
    						scanf("%d", &k);
    						if (si_ze[root] < k)
    							printf("Reject Get
    ");
    						else
    						{
    							int num = k_th(root, k);
    							printf("Get at %d
    ", 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;
    }




  • 相关阅读:
    事务与锁的一些总结
    NYOJ 73
    NYOJ 456
    Sleep函数
    NYOJ 488(素数环)
    NYOJ 308
    NYOJ 27
    NYOJ 325
    NYOJ 138
    求两个或N个数的最大公约数(gcd)和最小公倍数(lcm)的较优算法
  • 原文地址:https://www.cnblogs.com/AWCXV/p/7632261.html
Copyright © 2011-2022 走看看