【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
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.
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.
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.
6 10
New 2
New 5
New 2
New 2
Free 3
Get 1
Get 2
Get 3
Free 3
Reset
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 <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\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] == 'F')
{
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] == 'R')
{
printf("Reset Now\n");
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\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;
}
【23.68%】【hdu 2871】Memory Control的更多相关文章
- 【改革春风吹满地 HDU - 2036 】【计算几何-----利用叉积计算多边形的面积】
利用叉积计算多边形的面积 我们都知道计算三角形的面积时可以用两个邻边对应向量积(叉积)的绝对值的一半表示,那么同样,对于多边形,我们可以以多边形上的一个点为源点,作过该点并且过多边形其他点中的某一个的 ...
- 【23. 合并K个排序链表】【困难】【优先队列/堆排序】
合并 k 个排序链表,返回合并后的排序链表.请分析和描述算法的复杂度. 示例: 输入: [ 1->4->5, 1->3->4, 2->6] 输出: 1->1-> ...
- 【HDU 2255】奔小康赚大钱 (最佳二分匹配KM算法)
奔小康赚大钱 Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Subm ...
- 七夕节 (HDU - 1215) 【简单数论】【找因数】
七夕节 (HDU - 1215) [简单数论][找因数] 标签: 入门讲座题解 数论 题目描述 七夕节那天,月老来到数字王国,他在城门上贴了一张告示,并且和数字王国的人们说:"你们想知道你们 ...
- 【二分】【最长上升子序列】HDU 5489 Removed Interval (2015 ACM/ICPC Asia Regional Hefei Online)
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5489 题目大意: 一个N(N<=100000)个数的序列,要从中去掉相邻的L个数(去掉整个区间 ...
- 【贪心】【模拟】HDU 5491 The Next (2015 ACM/ICPC Asia Regional Hefei Online)
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5491 题目大意: 一个数D(0<=D<231),求比D大的第一个满足:二进制下1个个数在 ...
- 【动态规划】【二分】【最长上升子序列】HDU 5773 The All-purpose Zero
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5773 题目大意: T组数据,n个数(n<=100000),求最长上升子序列长度(0可以替代任何 ...
- 【动态规划】【KMP】HDU 5763 Another Meaning
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5763 题目大意: T组数据,给两个字符串s1,s2(len<=100000),s2可以被解读成 ...
- 【归并排序】【逆序数】HDU 5775 Bubble Sort
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5775 题目大意: 冒泡排序的规则如下,一开始给定1~n的一个排列,求每个数字在排序过程中出现的最远端 ...
随机推荐
- 【JZOJ4910】【NOIP2017模拟12.3】子串
题目描述 数据范围 =w= 暴力: 从前往后枚举一个i,再从前往后枚举一个j: 如果s[i]不是s[j]的子串,更新答案,继续枚举: 如果s[i]是s[j]的子串,停止枚举. 因为对于s[k] (k& ...
- Directx教程(30) 如何保证渲染物体不会变形
原文:Directx教程(30) 如何保证渲染物体不会变形 在Directx11教程(6)中, 我们曾经实现过这个功能,但那时是在SystemClass中,处理WM_SIZE时候,重新调用m ...
- @topcoder - SRM766R1 D1L3@ ShortestMissingSubsequences
目录 @description@ @solution@ @accepted code@ @details@ @description@ 给定一个大小为 G 的字符集,并给定一个长度为 N 的字符串 A ...
- Android 神兵利器之通过解析网页获取到的API数据合集,可拿来就用
AppApis 前段时间,写了个做app的实战系列教程,其中一篇章提到了解析网页中的数据为己所用,看到大家的响应还不错,于是把自己以前解析过的网页数据都整理了下,开放出来,给更多的人使用,希望可以帮助 ...
- MacOS利用Terminal访问远程Linux服务器
常用命令 默认22端口访问 ssh x.x.x.x 指定端口访问 ssh x.x.x.x -p port 指定用户访问(默认是MacOS当前用户) ssh user@x.x.x.x [-p port] ...
- Java练习 SDUT-2053_整理音乐
整理音乐 Time Limit: 1000 ms Memory Limit: 65536 KiB Problem Description 请用链表完成下面题目要求. xiaobai 很喜欢音乐,几年来 ...
- vuex之仓库数据的设置与获取
如果你之前使用过vue.js,你一定知道在vue中各个组件之间传值的痛苦,在vue中我们可以使用vuex来保存我们需要管理的状态值,值一旦被修改,所有引用该值的地方就会自动更新,那么接下来我们就来学习 ...
- 微信小程序记录
1.vs code 可以安装 Vetur-wepy 对代码高亮的提示. 2.取消swiper组件的手动滑动效果 在 swiper-item 中添加 catchtouchmove='catchTouch ...
- AutoCAD安装失败怎样卸载重新安装AutoCAD,解决AutoCAD安装失败的方法总结
技术帖:AutoCAD没有按照正确方式卸载,导致AutoCAD安装失败.楼主也查过网上关于如何解决AutoCAD安装失败的一些文章,是说删除几个AutoCAD文件和AutoCAD软件注册表就可以解决A ...
- OpenResty,X-WAF防火墙相关
>>OpenResty<< >>Lua教程<< >>反向代理百度百科<< >>X-WAF配置指南<<