【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的一个排列,求每个数字在排序过程中出现的最远端 ...
随机推荐
- TCP/IP 协议栈学习代码
全部代码 直接使用socket 客户端 import java.io.*; import java.net.Inet4Address; import java.net.InetSocketAddres ...
- oracle如何利用hostname方式连接数据库
host name方式只支持tcp/ip协议的小局域网 修改listener.ora中的如下信息 (SID_DESC = (GLOBAL_DBNAME = ur_hostname) --你的机器名 ( ...
- display: none;、visibility: hidden、opacity=0区别总结
display: none; 1.浏览器不会生成属性为display: none;的元素. 2.display: none;不占据空间(毕竟都不熏染啦),所以动态改变此属性时会引起重排. 3.disp ...
- windows.open window.location.href的用法和区别
window.location.href 只能在当前页面打开,不能用新窗口打开 windows.open("URL","窗口名称","窗口外观设定& ...
- WPF TextBox提示文字设定
WPF TextBox框提示文字,鼠标划入提示文字消失 <TextBox Width=" VerticalContentAlignment="Center" Bor ...
- oracle中常用的时间格式转换
1:取得当前日期是本月的第几周 select to_char(sysdate,'YYYYMMDD W HH24:MI:SS') from dual; TO_CHAR(SYSDATE,'YY') se ...
- php json_encode输出为空问题
这种情况一般是json_encode序列化数组时出现错误,错误原因有很多,可以通过json_last_error函数来查看错误原因!!! 可能的原因 ------------------------- ...
- 《DL/T 976-2017 带电作业用工具、装置和设备预防性试验规程》中的样品名称及试验项目
- day15 web前端之css
css的概念以及初体验 概念: CSS(cascading style sheet)也就是层叠样式表:它是一种网页设计的新技术,现在已经被大多数浏览器所支持,层位网页设计必不可少的工具之一.优点: ...
- day5-python之递归与二分法
一.递归的定义 递归调用是函数嵌套调用的一种特殊形式,函数在调用时,直接或间接调用了自身,就是递归调用 二.递归分为两个阶段:递推,回溯 age(5) = age(4) + 2 age(4) = ag ...