小Q与内存
Portal --> broken qwq
Description
(这个描述好像怎么都精简不起来啊qwq)
大概是说你的计算机有1GB的物理内存,按照Byte寻址,其物理地址空间为\(0\sim 2^{30}-1\),然后要支持以下三种操作:
(1)alloc k:申请一段长度为\(k\) byte的内存,申请成功返回该段内存的标号。分配方式为将当前未分配的地址中最小的\(k\)个依次分给该内存段(可能不连续),如果当前内存少于\(k\) byte,执行失败
(2)free i:释放标号为\(i\)的内存段,该内存段中的物理内存状态被重置为未分配,如果说该标号不存在或者说已经被释放则执行失败
(3)access i,p:计算标号为\(i\)的内存段的第\(p\)个byte的物理地址,如果标号不合法或者位置不合法执行失败。\(p\)从\(0\)开始计算
数据范围:多组数据,数据组数为\(T\),\(1<=n<=2*10^5,T<=5\)
Solution
这题首先有一个难点就是你的语文一定要很好== alloc操作中,不管申请成功与否,都是先开了一个新的内存段的qwq,所以不管有没有成功标号都要加一
然后我们就可以开始做题了qwq
这题有两种做法,可以选择用非旋treap或者线段树,虽然说两个算法在实际运行的时候表现都十分优秀但是。。貌似前者的复杂度。。有点问题==(具体我也不是很清楚qwq这个时候应该疯狂膜拜lyy)
接下来讲一下线段树的做法
其实这题和某道splay板题有点像【Portal -->】,大体的思路也是我可以用一个点来表示一整个区间,然后只有在要用的时候再将这个点要用的部分给。。切出来就好了
所以现在先确定一下我们要干的大概是什么:首先我们可以建一棵\(0\sim 2^{30}-1\)的线段树(初始的时候只是一个点,如果一定要建出来的话每个节点对应的区间长度应该是\(2\)的整数次幂),然后每次alloc我们从这棵线段树中切一部分出来分配到当前内存段下,每次free我们又将某段内存段对应的那一部分给接回大的线段树中,access就直接查找就好了
所以我们需要实现的是:分离(split)、合并(merge)、查找(query)
合并的话就是正常的线段树合并即可,没有什么特别的地方
至于其他的操作,首先先定义一些要维护的值:
为了方便表示区间,我们考虑给每个节点打上一个\(tag\),具体含义就是:如果说\(tag\)为\(-1\),那么说明这个节点的区间已经分裂出左儿子和右儿子了,具体的统计什么的要继续递归处理其左右儿子,否则表示这个区间还没有分裂出左儿子和右儿子,并且这个区间的长度是\(2^{tag}\)
同时我们用\(sz\)表示每个节点表示的区间中实际有多少个空置的叶子节点
至于如何将一个节点存的区间给建出来的话。。考虑实现一个pushdown,如果说当前节点还能分的话那么新建左右儿子,并且左右儿子的\(tag\)值应该是父亲的\(tag\)值\(-1\),这里注意一下我们需要区分不能继续往下分的节点和可以继续往下分但是当前没有分的节点(说白了就是两个目前都没有后继可是前者不可以继续往下递归),实现的时候可以一个的左右儿子都设为\(-1\),另一个都设为\(0\)
然后我们讲分离操作
假设我们要将前\(k\)个位置分离出来(其实也就是前\(k\)个叶子节点),我们其实相当于在线段树上面找到第\(k\)个位置,然后把沿路上所有”前面“的部分全部提出来,并且将这些部分与原来大的线段树的连边删掉,所以我们只要一路上经过的节点都复制一个出来(用来链接),然后如果说递归走到左儿子那么不需要进行操作,如果递归走到右儿子说明整个左子树都是应该被提出来的部分,所以左子树直接断开,\(k\)递归的时候处理就按照找第\(k\)小处理就好了,注意不管是哪种情况,当前节点的\(sz\)都要减去\(k\)(被切掉了)
接着是查询操作
查询的话,我们直接在查询的内存段对应的线段树里面找第\(k\)个位置(从\(0\)开始数),具体实现有点玄学,因为我们要求这个位置原本的物理地址,也就是这个在原来大的线段树中是第几个叶子节点,所以考虑维护一个\(ret\)值,表示递归到当前这层,在前面的有多少个区间(这样说有点抽象,看图):
然后如果说我们在查找的过程中遇到了一个没有分裂的节点\(x\)(也就是\(tag[x]\neq -1\)),那么就直接返回\(2^{tag[x]}*ret+k\)(注意这里的\(k\)是当前递归传进来的\(k\)并不是题目查询的原来那个值),如果说一直递归到最后(也就是底层,此时左右儿子都是\(-1\))没有遇到任何一个没有分裂的节点,那么直接\(ret\)就是答案了(因为是从\(0\)开始数的嘛)
然后就十分愉悦地做完了,时间复杂度的话。。各项操作都是均摊\(log\)的,总的复杂度\(O(nlogn)\)
代码大概长这个样子
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=2*(1e5)+10,SEG=N*100;
int n,T;
namespace Seg{/*{{{*/
int ch[SEG][2],sz[SEG],rt[N],tag[SEG];//tag>=0 -> full
int tot,cnt_rt,Rt;
void init(){cnt_rt=0; tot=1; tag[1]=30; sz[1]=1<<tag[1]; Rt=1;}
void newrt(){rt[++cnt_rt]=0;}
int newnode(int tg){
ch[++tot][0]=ch[tot][1]=0; tag[tot]=tg; sz[tot]=tg>=0?1<<tg:0;
return tot;
}
void pushdown(int x){
if (tag[x]==-1) return;
if (tag[x]){
ch[x][0]=newnode(tag[x]-1);
ch[x][1]=newnode(tag[x]-1);
}
else ch[x][0]=ch[x][1]=-1;
tag[x]=-1;
}
int _merge(int x,int y){
if (!x||!y) return x+y;
if (ch[x][0]!=-1){
ch[x][0]=_merge(ch[x][0],ch[y][0]);
ch[x][1]=_merge(ch[x][1],ch[y][1]);
}
sz[x]+=sz[y];
return x;
}
bool Free(int x){
if (x>cnt_rt||!rt[x]) return false;
Rt=_merge(Rt,rt[x]);
rt[x]=0;
return true;
}
void _split(int x,int &now,int k){
now=newnode(0); sz[now]=0;
pushdown(x);
if (ch[x][0]==-1) return;
tag[now]=-1; sz[now]=k;
sz[x]-=k;
if (ch[x][0]&&k<sz[ch[x][0]])
_split(ch[x][0],ch[now][0],k);
else{
k-=sz[ch[x][0]];
ch[now][0]=ch[x][0]; ch[x][0]=0;
_split(ch[x][1],ch[now][1],k);
}
}
bool alloc(int k){
newrt();
if (sz[Rt]<k) return false;
_split(Rt,rt[cnt_rt],k);
return true;
}
void _query(int x,int k,int &ret){
if (ch[x][0]==-1) return;
if (tag[x]!=-1) {ret=ret*(1<<tag[x])+k;return;}
ret*=2;
if (ch[x][0]&&k<sz[ch[x][0]]) _query(ch[x][0],k,ret);
else _query(ch[x][1],k-sz[ch[x][0]],++ret);
}
bool access(int x,int k,int &ret){
if (x>cnt_rt||!rt[x]||k>=sz[rt[x]]) return false;
ret=0;
_query(rt[x],k,ret);
return true;
}
}/*}}}*/
int main(){
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
#endif
int x,p,k,tmp,op;
scanf("%d",&T);
for (int o=1;o<=T;++o){
scanf("%d",&n);
Seg::init();
for (int i=1;i<=n;++i){
scanf("%d",&op);
if (op==1){
scanf("%d",&k);
if (Seg::alloc(k)) printf("ok\n");
else printf("failed\n");
}
else if (op==2){
scanf("%d",&x);
if (Seg::Free(x)) printf("ok\n");
else printf("failed\n");
}
else{
scanf("%d%d",&x,&p);
if (Seg::access(x,p,tmp)) printf("%d\n",tmp);
else printf("failed\n");
}
}
}
}
小Q与内存的更多相关文章
- (2016北京集训十)【xsy1530】小Q与内存
一道很有意思的神题~ 暴力平衡树的复杂度很对(并不),但是$2^{30}$的空间一脸屎 这题的正解是一个类似线段树的数据结构,我觉得很有创新性Orz 首先可以想到一种暴力就是用一个点代表一个区间,然后 ...
- [2016北京集训测试赛5]小Q与内存-[线段树的神秘操作]
Description Solution 哇真的异常服气..线段树都可以搞合并和拆分的啊orzorz.神的世界我不懂 Code #include<iostream> #include< ...
- 【二分图】ZJOI2007小Q的游戏
660. [ZJOI2007] 小Q的矩阵游戏 ★☆ 输入文件:qmatrix.in 输出文件:qmatrix.out 简单对比 时间限制:1 s 内存限制:128 MB [问题描述] ...
- 重庆OI2017 小 Q 的棋盘
小 Q 的棋盘 时间限制: 1 Sec 内存限制: 512 MB 题目描述 小Q正在设计一种棋类游戏.在小Q设计的游戏中,棋子可以放在棋盘上的格点中.某些格点之间有连线,棋子只能在有连线的格点之间移 ...
- 如何让手游内存占用更小?从内存消耗iOS实时统计开始
为什么iOS内存使用过多会崩溃,性能会下降?腾讯游戏学院专家Devlin在本文给了解释,如何让手游内存占用更小?从内存消耗iOS实时统计开始. 一.问题 在之前的手游项目中,内存使用过多,都开始崩溃了 ...
- 平面直接坐标系线段相交问题(小Q(钟神)的问题)
[问题描述] 小 Q 对计算几何有着浓厚的兴趣.他经常对着平面直角坐标系发呆,思考一些有趣的问题.今天,他想到了一个十分有意思的题目:首先,小 Q 会在?轴正半轴和?轴正半轴分别挑选?个点.随后,他将 ...
- hdu---(4515)小Q系列故事——世界上最遥远的距离(模拟题)
小Q系列故事——世界上最遥远的距离 Time Limit: 500/200 MS (Java/Others) Memory Limit: 65535/32768 K (Java/Others)T ...
- HD4505小Q系列故事——电梯里的爱情
Problem Description 细心的同事发现,小Q最近喜欢乘电梯上上下下,究其原因,也许只有小Q自己知道:在电梯里经常可以遇到他心中的女神HR. 电梯其实是个很暧昧的地方,只有在电梯里,小Q ...
- hdu4505小Q系列故事——电梯里的爱情
小Q系列故事——电梯里的爱情 Time Limit: 300/100 MS (Java/Others) Memory Limit: 65535/32768 K (Java/Others)Tota ...
随机推荐
- 180726-InfluxDB基本概念小结
InfluxDB基本概念小结 InfluxDB作为时序数据库,与传统的关系型数据库相比而言,还是有一些区别的,下面尽量以简单明了的方式介绍下相关的术语概念 I. 基本概念 mysql influxdb ...
- java基础学习总结--开篇
春去秋来,转眼间,参加工作快2年了.本来应该是3年,然在毕业的第一年,有试着从事过其他行业.最终结果是失败了.2016年又回来从事软件开发,转眼即将2年,在这期间有许多收获,当然也有彷徨迷茫的时候,人 ...
- rest_framework基础
简介 为什么要使用REST framework? Django REST framework 是一个强大且灵活的工具包,用以构建Web APIs. - 在线可视的API,对于赢得你的开发者们十分有用 ...
- 一个简单的rest_framework demo
models.py from django.db import models class UserInfo(models.Model): username = models.CharField(max ...
- java 不同数据类型的相互转化
在工作中经常会遇到需要将数据类型转化的情况,今天抽出时间总结一下. date——string Date date = new Date(); DateFormat dateformat = new S ...
- WCF传送大数据时的错误“ 超出最大字符串内容长度配额”
格式化程序尝试对消息反序列化时引发异常: 尝试对参数 http://tempuri.org/ 进行反序列化时出错: GetLzdtArticleResult.InnerException 消息是“反序 ...
- 第六周的PSP
本周PSP: 本周进度条: 累积进度图:: 本周PSP饼状图:
- 博弈--ZOJ 3084 S-Nim(SG)
题意: 首先输入K 表示一个集合的大小 之后输入集合 表示对于这对石子只能去这个集合中的元素的个数 之后输入 一个m 表示接下来对于这个集合要进行m次询问 之后m行 每行输入一个n 表示有n个堆 ...
- lintcode-491-回文数
491-回文数 判断一个正整数是不是回文数. 回文数的定义是,将这个数反转之后,得到的数仍然是同一个数. 注意事项 给的数一定保证是32位正整数,但是反转之后的数就未必了. 样例 11, 121, 1 ...
- 获取emacs安装的elpa包名称
| grep "./" | sed 's/\.\///g' | sed 's/-[0-9].*$//' | sort -u