小Q与内存 - 非旋Treap - 线段树合并

题目大意:你要维护内存分配,初始有一个空的内存池,编号从0到 2301 2 30 − 1 ,每次形如:
alloc k,表示申请k个单位内存,依次将目前空着的编号最小的k个单位内存(可以不连续)作为这一次申请的结果。
delete t,表示销毁第t次申请的内存,第t次申请的内存变为空。
query t k,表示询问第t次申请的内存里,编号第k小的编号是多少(k从0开始)。
期间还需要特判一些Fail的情况之类的。
题解:
考虑用平衡树维护内存池和每次申请的集合,像前一篇blog一样每个点表示一个区间,需要的时候再切开。那么如果使用基于随机的平衡树,一三两个操作是严格log的,然后第二个操作的单次复杂度是其会被内存池隔开的段数乘以log,似乎可以证明这个均摊不是很大,而且空间好像直接就是线性。
其实将平衡树改为线段树合并,那么一三两个操作仍然是严格log,并且增加不超过log个点,第二个操作的复杂度是两颗线段树的公共部分,而操作后这个公共部分会从总点数中扣去,所以第二个操作的总复杂度不超过总点数,因此复杂度是一个log。唯一的问题是空间复杂度不是线性(吧)。
先贴非旋Treap实现:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<utility>
#include<climits>
#include<assert.h>
#define gc getchar()
#define mp make_pair
#define fir first
#define sec second
#define N 5000010
#define mod 998244353
#define Fail { Failed();continue; }
#define len(x) (R[x]-L[x]+1)
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
using namespace std;
typedef pair<int,int> pii;
inline int inn()
{
    int x,ch;while((ch=gc)<'0'||ch>'9');
    x=ch^'0';while((ch=gc)>='0'&&ch<='9')
        x=(x<<1)+(x<<3)+(ch^'0');return x;
}
struct Rand{
    int x,y;Rand(){x=1;}
    inline int operator()(){ return y=x,x+=x,(x>=mod?x-=mod:0),x+=y,(x>=mod?x-=mod:0),x; }
}rnd;
int sz[N],L[N],R[N],ch[N][2],fa[N],mn[N],mx[N],key[N],node_cnt,be_fr[200010];
inline int push_up(int x)
{
    return sz[x]=len(x)+sz[ch[x][0]]+sz[ch[x][1]],
    mn[x]=min(L[x],min(mn[ch[x][0]],mn[ch[x][1]])),
    mx[x]=max(R[x],max(mx[ch[x][0]],mx[ch[x][1]])),0;
}
inline int new_node(int _L,int _R) { int x=++node_cnt;return ch[x][0]=ch[x][1]=fa[x]=0,key[x]=rnd(),L[x]=_L,R[x]=_R,push_up(x),x; }
inline int setc(int x,int y,int z) { if(!x) return fa[y]=0;ch[x][z]=y;if(y) fa[y]=x;push_up(x); }
int merge(int x,int y)
{
    if(!x||!y) return x+y;
    if(key[x]<key[y]) return setc(x,merge(ch[x][1],y),1),x;
    return setc(y,merge(x,ch[y][0]),0),y;
}
pii splitk(int x,int k)
{
    if(!x||!k) return mp(0,x);pii t;
    if(k<=sz[ch[x][0]]) return t=splitk(ch[x][0],k),setc(x,t.sec,0),mp(t.fir,x);
    return t=splitk(ch[x][1],max(k-sz[ch[x][0]]-len(x),0)),setc(x,t.fir,1),mp(x,t.sec);
}
pii splitv(int x,int k)
{
    if(!x||k<mn[x]) return mp(0,x);pii t;
    if(k<L[x]) return t=splitv(ch[x][0],k),setc(x,t.sec,0),mp(t.fir,x);
    return t=splitv(ch[x][1],k),setc(x,t.fir,1),mp(x,t.sec);
}
inline pii cutk(int x,int k)
{
    pii a=splitk(x,k);int y=a.fir,l,r,ls;
    if(sz[y]==k) return a;while(ch[y][1]) y=ch[y][1];
    ls=len(y)-(sz[a.fir]-k),l=L[y]+ls,r=R[y],R[y]=l-1;
    while(y) push_up(y),y=fa[y];
    return mp(a.fir,merge(new_node(l,r),a.sec));
}
inline pii cutv(int x,int k)
{
    pii a=splitv(x,k);int y=a.fir,l,r;
    if(mx[y]<=k) return a;while(ch[y][1]) y=ch[y][1];
    l=k+1,r=R[y],R[y]=l-1;while(y) push_up(y),y=fa[y];
    return mp(a.fir,merge(new_node(l,r),a.sec));
}
struct Treap{
    int rt;
    inline int Union(Treap &t)
    {
        pii a,b;
        while(t.rt)
            a=cutv(rt,mn[t.rt]),b=cutv(t.rt,mn[a.sec]),
            t.rt=b.sec,rt=merge(merge(a.fir,b.fir),a.sec);
        return 0;
    }
    inline int query(int p)
    {
        pii a=cutk(rt,p);int ans=mx[a.fir];
        return rt=merge(a.fir,a.sec),ans;
    }
    inline int show()
    {
        return debug(rt)sp,debug(sz[rt])sp,debug(L[rt])sp,debug(R[rt])sp,debug(ch[rt][0])sp,debug(ch[rt][1])ln,0;
    }
}pl,rt[200010];
inline int Failed() { return puts("failed"),0; }inline int OK() { return puts("ok"),0; }
int main()
{
    R[0]=mx[0]=-1,L[0]=mn[0]=INT_MAX;
    for(int T=inn(),ttt=1;T;T--,ttt++)
    {
        int n=inn(),cnt=0;node_cnt=0;
        pl.rt=new_node(0,(1<<30)-1);
        while(n--)
        {
            int opt=inn();
            if(opt==1)
            {
                int s=inn(),x=++cnt;be_fr[x]=0;
                if(sz[pl.rt]<s) be_fr[x]=1,rt[x].rt=0;
                if(sz[pl.rt]<s) Fail else OK();
                pii t=cutk(pl.rt,s);
                rt[x].rt=t.fir,pl.rt=t.sec;
            }
            else if(opt==2)
            {
                int x=inn();
                if(x>cnt||be_fr[x]) Fail else OK();
                be_fr[x]=1;pl.Union(rt[x]);
            }
            else{
                int x=inn(),p=inn()+1;
                if(x>cnt||be_fr[x]||p>sz[rt[x].rt]) Fail
                printf("%d\n",rt[x].query(p));
            }
        }
    }
    return 0;
}

线段树合并


#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define N 10000010
#define gc getchar()
#define mp make_pair
#define fir first
#define sec second
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
using namespace std;
typedef pair<int,int> pii;
const int INF=1<<30;
inline int inn()
{
    int x,ch;while((ch=gc)<'0'||ch>'9');
    x=ch^'0';while((ch=gc)>='0'&&ch<='9')
        x=(x<<1)+(x<<3)+(ch^'0');return x;
}
int ch[N][2],sz[N],node_cnt;
inline int new_node(int l,int x=0) { return sz[x=++node_cnt]=l,ch[x][0]=ch[x][1]=0,x; }
int merge(int x,int y)
{
    if(!x||!y) return x+y;
    ch[x][0]=merge(ch[x][0],ch[y][0]);
    ch[x][1]=merge(ch[x][1],ch[y][1]);
    return sz[x]=sz[ch[x][0]]+sz[ch[x][1]],x;
}
pii split(int x,int k,int l,int r)
{
//  debug(x)sp,debug(k)sp,debug(l)sp,debug(r)sp,debug(ch[x][0])sp,debug(ch[x][1])sp,debug(sz[x])sp,debug(sz[ch[x][0]])sp,debug(sz[ch[x][1]])ln;
    pii t;if(k==sz[x]) return mp(x,0);pii res;int mid=(l+r)>>1,y;
    if(!ch[x][0]&&!ch[x][1]) ch[x][0]=new_node(mid-l+1),ch[x][1]=new_node(r-mid);y=new_node(0);
    if(k<=sz[ch[x][0]]) t=split(ch[x][0],k,l,mid),ch[x][0]=t.fir,ch[y][0]=t.sec,ch[y][1]=ch[x][1],ch[x][1]=0;
    else t=split(ch[x][1],k-sz[ch[x][0]],mid+1,r),ch[x][1]=t.fir,ch[y][1]=t.sec,ch[y][0]=0;
    sz[x]=sz[ch[x][0]]+sz[ch[x][1]],sz[y]=sz[ch[y][0]]+sz[ch[y][1]];return mp(x,y);
}
int query(int x,int k,int l,int r)
{
//  debug(x)sp,debug(k)sp,debug(l)sp,debug(r)ln;
    if(!ch[x][0]&&!ch[x][1]) return l+k-1;int mid=(l+r)>>1;
    if(!ch[x][0]&&!ch[x][1]) ch[x][0]=new_node(mid-l+1),ch[x][1]=new_node(r-mid);
    if(k<=sz[ch[x][0]]) return query(ch[x][0],k,l,mid);
    else return query(ch[x][1],k-sz[ch[x][0]],mid+1,r);
}
inline int Fail() { return !printf("failed\n"); }
inline int OK() { return !printf("ok\n"); }
int be_fr[200010],rt[200010],pl;
int main()
{
    for(int T=inn();T;T--)
    {
        int n=inn(),cnt=0;node_cnt=0,pl=new_node(INF);
        for(int i=1;i<=n;i++) rt[i]=0;
        while(n--)
        {
            int opt=inn();
            if(opt==1)
            {
                int k=inn(),x=++cnt;pii t;
                if(k>sz[pl]) { Fail(),be_fr[x]=1;continue; }
                OK(),be_fr[x]=0,t=split(pl,k,1,INF),rt[x]=t.fir,pl=t.sec;
            }
            else if(opt==2)
            {
                int x=inn();
                if(x>cnt||be_fr[x]) { Fail();continue; }
                OK(),be_fr[x]=1,pl=merge(pl,rt[x]);
            }
            else{
                int x=inn(),k=inn()+1;
                if(x>cnt||be_fr[x]||k>sz[rt[x]]) { Fail();continue; }
                printf("%d\n",query(rt[x],k,1,INF)-1);
            }
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值