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与内存的更多相关文章

  1. (2016北京集训十)【xsy1530】小Q与内存

    一道很有意思的神题~ 暴力平衡树的复杂度很对(并不),但是$2^{30}$的空间一脸屎 这题的正解是一个类似线段树的数据结构,我觉得很有创新性Orz 首先可以想到一种暴力就是用一个点代表一个区间,然后 ...

  2. [2016北京集训测试赛5]小Q与内存-[线段树的神秘操作]

    Description Solution 哇真的异常服气..线段树都可以搞合并和拆分的啊orzorz.神的世界我不懂 Code #include<iostream> #include< ...

  3. 【二分图】ZJOI2007小Q的游戏

    660. [ZJOI2007] 小Q的矩阵游戏 ★☆   输入文件:qmatrix.in   输出文件:qmatrix.out   简单对比 时间限制:1 s   内存限制:128 MB [问题描述] ...

  4. 重庆OI2017 小 Q 的棋盘

    小 Q 的棋盘 时间限制: 1 Sec  内存限制: 512 MB 题目描述 小Q正在设计一种棋类游戏.在小Q设计的游戏中,棋子可以放在棋盘上的格点中.某些格点之间有连线,棋子只能在有连线的格点之间移 ...

  5. 如何让手游内存占用更小?从内存消耗iOS实时统计开始

    为什么iOS内存使用过多会崩溃,性能会下降?腾讯游戏学院专家Devlin在本文给了解释,如何让手游内存占用更小?从内存消耗iOS实时统计开始. 一.问题 在之前的手游项目中,内存使用过多,都开始崩溃了 ...

  6. 平面直接坐标系线段相交问题(小Q(钟神)的问题)

    [问题描述] 小 Q 对计算几何有着浓厚的兴趣.他经常对着平面直角坐标系发呆,思考一些有趣的问题.今天,他想到了一个十分有意思的题目:首先,小 Q 会在?轴正半轴和?轴正半轴分别挑选?个点.随后,他将 ...

  7. hdu---(4515)小Q系列故事——世界上最遥远的距离(模拟题)

    小Q系列故事——世界上最遥远的距离 Time Limit: 500/200 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others)T ...

  8. HD4505小Q系列故事——电梯里的爱情

    Problem Description 细心的同事发现,小Q最近喜欢乘电梯上上下下,究其原因,也许只有小Q自己知道:在电梯里经常可以遇到他心中的女神HR. 电梯其实是个很暧昧的地方,只有在电梯里,小Q ...

  9. hdu4505小Q系列故事——电梯里的爱情

    小Q系列故事——电梯里的爱情 Time Limit: 300/100 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others)Tota ...

随机推荐

  1. 第四篇 前端学习之JQuery基础

    一 jQuery是什么? jQuery就是一个JavaScript的库. <1> jQuery由美国人John Resig创建,至今已吸引了来自世界各地的众多 javascript高手加入 ...

  2. HP VC模块Shared uplink Sets配置参考

    首先配置MAC地址的分配方式 在左侧导航栏中,点解"MAC Addresses" 选择VC分配MAC地址,并且选择一个合适的地址段,点击"Apply"继续 在弹 ...

  3. Ubuntu系统下在PyCharm里用virtualenv集成TensorFlow

    我的系统环境 Ubuntu 18.04 Python3.6 PyCharm 2018.3.2 community(免费版) Java 1.8 安装前准备 由于众所周知的原因,安装中需要下载大量包,尽量 ...

  4. Matplotlib外观和基本配置笔记

    title: matplotlib 外观和基本配置笔记 notebook: Python tags:matplotlib --- 参考资料,如何使用matplotlib绘制出数据图形,参考另一篇mat ...

  5. pyqt5实现SMTP邮件发送

    # -*- coding: utf-8 -*- # Form implementation generated from reading ui file 'SMTP.ui' # # Created b ...

  6. mysql group by 取第一条

    select * from table where id in (select max(id) from table group by sku) 说明:id是自增序列,sku是表中的一个字段

  7. Hyper-V虚拟机联网设置

    转自:http://www.3lian.com/edu/2012/12-22/50492.html Windows 8中内置的Hyper-V管理器可以说给许多人带来了惊喜!在Hyper-V管理器强大的 ...

  8. Python基础知识-06-集合内存布尔False

    python其他知识目录 1.判断一个字符串中是否有敏感字符? #str: m_str="我叫魔降风云变" if "魔" in m_str: #判断指定字符是否 ...

  9. loadrunner socket协议问题归纳(1)

    前段时间测了loadrunner直接发送报文到socket上的性能测试.在此,稍微回顾整理下. 与socket通讯,有两种方式,一种是建立长连接,建立后,不停的发送,接收.另外一种是建立短连接,建立连 ...

  10. 对石家庄铁道大学网站UI的分析

         作为我们团队的PM,老师对我们提出了一些额外的要求,所以我发表这篇博客来谈一下对石家庄铁道大学网站UI的分析.      首先,PM 对项目所有功能的把握, 特别是UI.最差的UI, 体现了 ...