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. 使用Python的Requests库进行web接口测试

    1.Requests简介Requests 是使用 Apache2 Licensed 许可证的 HTTP 库.用 Python 编写,真正的为人类着想. Python 标准库中的 urllib2 模块提 ...

  2. 【python 3.6】笨办法取出列表中的字典的value

    #python 3.6 #!/usr/bin/env python # -*- coding:utf-8 -*- __author__ = 'BH8ANK' x = {'RegionSet': [{' ...

  3. Python 代码调试技巧

    使用 pdb 进行调试 pdb 是 python 自带的一个包,为 python 程序提供了一种交互的源代码调试功能,主要特性包括设置断点.单步调试.进入函数调试.查看当前代码.查看栈片段.动态改变变 ...

  4. KETTLE并行

    1.转换的并行 转换的并行是改变复制的数量 上面的转换相当于下面的: 实际是把一个任务拆成三部分执行,相当于在一个数据库连接中做了三次查询,数据库连接的开销没有增加,但是有三个进程一起执行. 2.jo ...

  5. HDU 5869 Different GCD Subarray Query rmq+离线+数状数组

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5869 Different GCD Subarray Query Time Limit: 6000/3 ...

  6. 小白用Android MVP-初体验(一)

    写android以来,一直都是采用MVC的模式,所有的业务逻辑,网络请求等都放在了View中,即Activity或者Fragment中.看了一些mvp文章,很多跨度较大,也因为自己造诣不够,还不能跟上 ...

  7. lintcode-411-格雷编码

    411-格雷编码 格雷编码是一个二进制数字系统,在该系统中,两个连续的数值仅有一个二进制的差异. 给定一个非负整数 n ,表示该代码中所有二进制的总数,请找出其格雷编码顺序.一个格雷编码顺序必须以 0 ...

  8. iOS- 如何建立索引实现本地文本搜索引擎,允许容错搜索?

    1.前言 实现一个本地搜索引擎,允许容错搜索,也就是搜索结果不需要和搜索的关键字完全精准匹配.比如,搜索”eric wang“,搜索结果可以包括Erica Watts等等.搜索效率十分高. 这里我们需 ...

  9. 0422 寻找数学口袋精灵BUG

    首先要部署这个app项目就是第一步: 一.前提下载并安装JDK 在线图解:手把手教你安装JDK      http://www.lvtao.net/server/windows-setup-jdk.h ...

  10. 【Leetcode】 328. Odd Even Linked List

    Given a singly linked list, group all odd nodes together followed by the even nodes. Please note her ...