HDU 2871 Memory Control(线段树:区间合并)

273 篇文章 0 订阅
112 篇文章 0 订阅

HDU 2871 Memory Control(线段树:区间合并)

http://acm.hdu.edu.cn/showproblem.php?pid=2871

分析:

       首先分析题目中能进行的几种操作:

1.    Reset: 所有内存单元置0

2.    New x:申请一块包含连续x个单元的空闲内存块

3.    Freex: 释放包含x号单元的(已被占用)内存块

4.    Get x:返回第x个被占用的内存块的起始地址.

 

根据上面的分析我们知道维护一棵线段树,每个节点应该具有的

信息是:

1.    cover[i](根本信息) : 为0或i或-1,为0时表示未被占用,为i时的含义是:对于题目中的第i条New命令,如果申请成功就置所有被申请单元的cover位为i, 为-1时表示儿子节点的cover位不一致

2.    sub[i]:表示i节点控制区间的最长连续空闲区间长度,.

3.    pre[i]: i节点控制区间的最长前缀连续空闲区间的长度

4.    suf[i]:i节点控制区间的最长后缀连续空闲区间的长度

 

上面4种信息能解决reset和new的问题.下面还有几个需要全局维护的信息,由于命令最多5W条,所以就算cover位的值最多为5W,所以需要维护的信息有:

1.    Interval[MAXN]:和sub一样是二元组(x,y)的结构体,Interval[i]包含了第i个New操作所申请到的区间的起始坐标x和终止坐标y.注意这里的第i个并不代表该(x,y)在整个内存中是从左到右的第i个位置.而且这Interval信息就算Update更新了线段树,也不用更改.

这个Interval信息,是在query_sub之后得到区间后更新的.

如果要执行Free x操作,先查询到x单元的cover位的值,如果非0则就可以在interval中找到区间[L,R],然后用update把区间置0即可.

如果要执行Get x操作,只需要用一个hash数组标记,遍历一遍线段树找到第x个出现的cover位即可,然后在interval中查询其实坐标x.

 

线段树的具体实现:

1.    build(i,l,r): 建立一棵以i节点为根的线段树.

如果l==r,那么更新cover,sub,pre,suf信息

否则递归建立左右子树,然后PushUp操作.

2.    PushDown(i,l,r):如果cover[i]!=-1,将cover位传递下去,更新子节点的suf,sub,pre信息.

3.    PushUp(i,l,r):有子树信息更新父节点的cover,sub,suf,pre信息.

4.    update(ql,qr,v,i,l,r):将[ql,qr]与[l,r]重叠部分区间cover位置v值.

如果ql<=l&&r<=qr,那么直接置cover,更新sub,suf,pre.否则先PushDown,然后递归update左右子树,最后PushUp.

5.    query_free(x,i,l,r):找到x单元所在的内存块.首先x肯定在[l,r]中.

如果当前cover[i]!=-1,那么直接返回cover位(为0,则Reject free).否则PushDown后递归查询左右子树.

6.    query_get(x,i,l,r):找到从左到右第x块被占用的内存块的cover位编号记录在cnt_num中.

首先每次query_get前都要讲hash置false,然后用cnt记录当前已经找过了多少块被占用的内存块.用cnt_num记录那个第x块被占用的内存快的cover位.

如果cnt>=x了,那么直接返回即可.

如果当前cover!=-1,那么直接处理返回不递归,如果cover==-1,还需要递归查询子树.

7.    query(ql,qr,x,i,l,r):返回[ql,qr]与[l,r]公共部分的连续空闲区间大小>=x的那个起始地址最小的区间的起始坐标

首先如果sub[i]<x,直接返回-1.

注意:如果l==r的话也要立刻return否则无限递归.

PushDown操作之后,

如果sub[i*2]>=x,那么直接返回qeury(ql,qr,lson).

否则如果L2=(左儿子的最长后缀起始坐标x, 右儿子的最长前缀终点坐标y),L2区间的长度>=x,那么也直接返回L2.

最后只能(也必定)直接返回query(ql,qr,rson).

注意:使用new的时候,query这里查询返回的是区间[L,R],且该区间>=x,所以我们之后在main中还需要update 更新区间[L,L+x-1]

由于在query函数中没有加上l==r时返回,导致在New 1操作的时候会无限递归,一致RTE(存取错误)了半天.

AC代码:359ms

#include <iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
const int MAXN=50000+1000;
#define lson i*2,l,m
#define rson i*2+1,m+1,r
#define root 1,1,n

struct Interval
{
    int x,y;

}interval[MAXN];
int cover[MAXN*4],sub[MAXN*4],suf[MAXN*4],pre[MAXN*4];
bool hash[MAXN];
int cnt;//初值为0
int cnt_num;
int all;//记录当前内存中一共有多少块内存块
void PushDown(int i,int l,int r)
{
    int m=(l+r)/2;
    if(cover[i]!=-1)
    {
        cover[i*2]=cover[i*2+1]=cover[i];
        sub[i*2]=suf[i*2]=pre[i*2]= (cover[i]?0:m-l+1);
        sub[i*2+1]=suf[i*2+1]=pre[i*2+1]= (cover[i]?0:r-m);
    }
}
void PushUp(int i,int l,int r)
{
    int m=(l+r)/2;
    //cover
    if(cover[i*2]==-1 || cover[i*2+1]==-1)
        cover[i]=-1;
    else if(cover[i*2] != cover[i*2+1])
        cover[i]=-1;
    else
        cover[i]=cover[i*2];

    //sub
    sub[i]=max(suf[i*2]+pre[i*2+1],max(sub[i*2],sub[i*2+1]));

    //pre
    pre[i]=pre[i*2];
    if(pre[i]==m-l+1)pre[i]+=pre[i*2+1];

    //suf
    suf[i]=suf[i*2+1];
    if(suf[i]==r-m)suf[i] += suf[i*2];
}
void build(int i,int l,int r)
{
    if(l==r)
    {
        cover[i]=0;
        sub[i]=suf[i]=pre[i]=1;
        return ;
    }
    int m=(l+r)/2;
    build(lson);
    build(rson);
    PushUp(i,l,r);
}
void update(int ql,int qr,int v,int i,int l,int r)
{
    if(ql<=l&&r<=qr)
    {
        cover[i]=v;
        suf[i]=sub[i]=pre[i]= (v?0:r-l+1);
        return ;
    }
    PushDown(i,l,r);
    int m=(l+r)/2;
    if(ql<=m) update(ql,qr,v,lson);
    if(m<qr) update(ql,qr,v,rson);
    PushUp(i,l,r);
}
int query_free(int x,int i,int l,int r)//x肯定被区间[l,r]包含
{
    if(cover[i]!=-1)
        return cover[i];
    PushDown(i,l,r);
    int m=(l+r)/2;
    if(x<=m) return query_free(x,lson);
    return query_free(x,rson);
}
void query_get(int x,int i,int l,int r)
{
    if(cnt>=x)
        return ;
    if(cover[i]!=-1)
    {
        if(cover[i]>0)
        {
            if(hash[cover[i]]==false)
            {
                hash[cover[i]]=true;
                cnt++;
            }
            if(cnt==x) cnt_num = cover[i];
        }
        return ;
    }
    int m =(l+r)/2;
    query_get(x,lson);
    if(cnt>=x)
        return ;
    query_get(x,rson);
}
int query(int ql,int qr,int x,int i,int l,int r)//返回满足需求的区间左端点坐标
{
    if(sub[i]<x)
        return -1;
    if(l==r)//这句话不加的话,就将在New 1的时候无限递归,RTE(存取错误)
        return l;
    PushDown(i,l,r);
    int m=(r+l)/2;
    if(sub[i*2]>=x) return query(ql,qr,x,lson);
    else
    {
        int len_l= min(suf[i*2],m-ql+1);//左子树的后缀在[ql,qr]内的公共最长长度
        int len_r= min(pre[i*2+1],qr-m);//右子树的前缀在[ql,qr]内的公共最长长度
        if(len_l+len_r >=x)
            return m-len_l+1;
    }
    return query(ql,qr,x,rson);
}

int main()
{
    int n,m;
    while(scanf("%d%d",&n,&m)==2&&n&&m)
    {
        all=0;//当前内存一共0块内存被占用
        build(root);
        for(int i=1;i<=m;i++)
        {
            char str[10];
            int x;
            scanf("%s",str);
            if(str[0]=='R')
            {
                all=0;//被占用的块清0
                update(1,n,0,root);
                printf("Reset Now\n");
            }
            else if(str[0]=='N')
            {
                scanf("%d",&x);
                if(sub[1]<x)
                    printf("Reject New\n");
                else
                {
                    int L=query(1,n,x,root);
                    printf("New at %d\n",L);
                    update(L,L+x-1,i,root);
                    interval[i].x=L;
                    interval[i].y=L+x-1;
                    all++;//被占用内存多了1块
                }
            }
            else if(str[0]=='F')
            {
                scanf("%d",&x);
                if(x>n||x<=0)
                {
                    printf("Reject Free\n");
                    continue;
                }
                int id=query_free(x,root);
                if(id==0)
                    printf("Reject Free\n");
                else
                {
                    int ql=interval[id].x,qr=interval[id].y;
                    printf("Free from %d to %d\n",ql,qr);
                    update(ql,qr,0,root);
                    all--;
                }
            }
            else if(str[0]=='G')
            {
                scanf("%d",&x);
                if(x>all)
                    printf("Reject Get\n");
                else
                {
                    memset(hash,false,sizeof(hash));
                    cnt=0;
                    cnt_num=0;
                    query_get(x,root);
                    printf("Get at %d\n",interval[cnt_num].x);
                }
            }
        }
        printf("\n");
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值